codesearch-mcp 0.3.0-alpha → 0.4.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 +59 -6
- package/LICENSE +661 -21
- package/README.md +33 -17
- package/dist/app.js +13 -20
- package/dist/src/ast/CallExtractor.d.ts +8 -0
- package/dist/src/ast/CallExtractor.js +68 -0
- package/dist/src/ast/GraphqlNameExtractor.d.ts +22 -0
- package/dist/src/ast/GraphqlNameExtractor.js +240 -0
- package/dist/src/ast/ImportEdgeExtractor.d.ts +11 -0
- package/dist/src/ast/ImportEdgeExtractor.js +129 -0
- package/dist/src/ast/IndexedNodeResolver.d.ts +14 -0
- package/dist/src/ast/IndexedNodeResolver.js +217 -0
- package/dist/src/ast/MemberExtractor.d.ts +8 -0
- package/dist/src/ast/MemberExtractor.js +129 -0
- package/dist/src/ast/TopLevelSymbolExtractor.d.ts +5 -0
- package/dist/src/ast/TopLevelSymbolExtractor.js +86 -0
- package/dist/src/ast/graphql-grammar.d.ts +24 -0
- package/dist/src/ast/graphql-grammar.js +93 -0
- package/dist/src/ast/node-helpers.d.ts +11 -0
- package/dist/src/ast/node-helpers.js +55 -0
- package/dist/src/commands/EvalCommand.d.ts +7 -3
- package/dist/src/commands/EvalCommand.js +36 -158
- package/dist/src/commands/IndexCommand.d.ts +10 -4
- package/dist/src/commands/IndexCommand.js +52 -124
- package/dist/src/commands/McpCommand.d.ts +6 -3
- package/dist/src/commands/McpCommand.js +19 -14
- package/dist/src/commands/WebCommand.d.ts +9 -3
- package/dist/src/commands/WebCommand.js +17 -8
- package/dist/src/commands/eval/baseline-comparison.d.ts +6 -0
- package/dist/src/commands/eval/baseline-comparison.js +54 -0
- package/dist/src/commands/eval/eval-args.d.ts +6 -0
- package/dist/src/commands/eval/eval-args.js +71 -0
- package/dist/src/commands/eval/eval-corpora.d.ts +6 -0
- package/dist/src/commands/eval/eval-corpora.js +76 -0
- package/dist/src/commands/eval/eval-metadata.d.ts +17 -0
- package/dist/src/commands/eval/eval-metadata.js +75 -0
- package/dist/src/commands/eval/index-inspector.d.ts +3 -0
- package/dist/src/commands/eval/index-inspector.js +21 -0
- package/dist/src/commands/eval/staged-search.d.ts +3 -0
- package/dist/src/commands/eval/staged-search.js +20 -0
- package/dist/src/commands/eval/suite-loader.d.ts +8 -0
- package/dist/src/commands/eval/suite-loader.js +52 -0
- package/dist/src/commands/index/backend-probe.d.ts +2 -0
- package/dist/src/commands/index/backend-probe.js +15 -0
- package/dist/src/commands/index/embedding-setup.d.ts +3 -0
- package/dist/src/commands/index/embedding-setup.js +16 -0
- package/dist/src/commands/index/index-summary.d.ts +2 -0
- package/dist/src/commands/index/index-summary.js +22 -0
- package/dist/src/commands/index/legacy-ollama-guard.d.ts +3 -0
- package/dist/src/commands/index/legacy-ollama-guard.js +23 -0
- package/dist/src/commands/index/prune-step.d.ts +3 -0
- package/dist/src/commands/index/prune-step.js +22 -0
- package/dist/src/commands/index/scan-targets.d.ts +8 -0
- package/dist/src/commands/index/scan-targets.js +107 -0
- package/dist/src/config-store.d.ts +0 -1
- package/dist/src/config-store.js +3 -4
- package/dist/src/config.d.ts +1 -1
- package/dist/src/config.js +1 -1
- package/dist/src/constants.d.ts +15 -0
- package/dist/src/constants.js +34 -6
- package/dist/src/container.d.ts +4 -2
- package/dist/src/container.js +7 -5
- package/dist/src/db/drizzle-types.d.ts +7 -0
- package/dist/src/db/index.d.ts +1 -0
- package/dist/src/db/schema-steps.d.ts +8 -0
- package/dist/src/db/schema-steps.js +105 -0
- package/dist/src/declarations/DeclarationTree.d.ts +1 -1
- package/dist/src/eval-results.js +16 -2
- package/dist/src/eval-vector-index.d.ts +5 -0
- package/dist/src/eval-vector-index.js +60 -0
- package/dist/src/help.js +9 -1
- package/dist/src/index-args.d.ts +13 -0
- package/dist/src/index-args.js +48 -0
- package/dist/src/indexing/AnalysisBatcher.d.ts +12 -0
- package/dist/src/indexing/AnalysisBatcher.js +28 -0
- package/dist/src/indexing/ChunkEmbedder.d.ts +24 -0
- package/dist/src/indexing/ChunkEmbedder.js +37 -0
- package/dist/src/indexing/ConsoleScanReporter.d.ts +10 -0
- package/dist/src/indexing/ConsoleScanReporter.js +73 -0
- package/dist/src/indexing/EmbeddingSchemaPreparer.d.ts +14 -0
- package/dist/src/indexing/EmbeddingSchemaPreparer.js +66 -0
- package/dist/src/indexing/FileIngestor.d.ts +22 -0
- package/dist/src/indexing/FileIngestor.js +116 -0
- package/dist/src/indexing/IndexStore.d.ts +24 -0
- package/dist/src/indexing/IndexStore.js +69 -0
- package/dist/src/indexing/StaleFilePruner.d.ts +8 -0
- package/dist/src/indexing/StaleFilePruner.js +55 -0
- package/dist/src/indexing/analysis-rows.d.ts +4 -0
- package/dist/src/indexing/analysis-rows.js +65 -0
- package/dist/src/indexing/project-notices.d.ts +2 -0
- package/dist/src/indexing/project-notices.js +63 -0
- package/dist/src/interfaces/IAstParser.d.ts +2 -1
- package/dist/src/interfaces/ICallGraph.d.ts +28 -0
- package/dist/src/interfaces/ICallGraph.js +2 -0
- package/dist/src/interfaces/IChunkSearch.d.ts +42 -0
- package/dist/src/interfaces/IChunkSearch.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +6 -2
- package/dist/src/interfaces/IDependencyGraph.d.ts +24 -0
- package/dist/src/interfaces/IDependencyGraph.js +2 -0
- package/dist/src/interfaces/IIndexIdentityGuard.d.ts +3 -0
- package/dist/src/interfaces/IIndexIdentityGuard.js +2 -0
- package/dist/src/interfaces/IIndexedNodeResolver.d.ts +10 -0
- package/dist/src/interfaces/IIndexedNodeResolver.js +2 -0
- package/dist/src/interfaces/IMcpReadiness.d.ts +12 -0
- package/dist/src/interfaces/IMcpReadiness.js +2 -0
- package/dist/src/interfaces/IProjectCatalog.d.ts +13 -0
- package/dist/src/interfaces/IProjectCatalog.js +2 -0
- package/dist/src/interfaces/IProjectRoots.d.ts +3 -0
- package/dist/src/interfaces/IProjectRoots.js +2 -0
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +21 -2
- package/dist/src/interfaces/IScanReporter.d.ts +13 -0
- package/dist/src/interfaces/IScanReporter.js +2 -0
- package/dist/src/interfaces/ISymbolLookup.d.ts +49 -0
- package/dist/src/interfaces/ISymbolLookup.js +2 -0
- package/dist/src/interfaces/IToolPaths.d.ts +62 -0
- package/dist/src/interfaces/IToolPaths.js +2 -0
- package/dist/src/interfaces/IWorkspaceFiles.d.ts +28 -0
- package/dist/src/interfaces/IWorkspaceFiles.js +2 -0
- package/dist/src/interfaces/SymbolKind.d.ts +1 -0
- package/dist/src/interfaces/SymbolKind.js +2 -0
- package/dist/src/interfaces/index.d.ts +12 -1
- package/dist/src/project-scope.d.ts +1 -0
- package/dist/src/project-scope.js +5 -0
- package/dist/src/retrieval-eval.d.ts +2 -1
- package/dist/src/retrieval-eval.js +45 -2
- package/dist/src/search/CallGraph.d.ts +9 -0
- package/dist/src/search/CallGraph.js +84 -0
- package/dist/src/search/ChunkSearch.d.ts +23 -0
- package/dist/src/search/ChunkSearch.js +115 -0
- package/dist/src/search/DependencyGraph.d.ts +19 -0
- package/dist/src/search/DependencyGraph.js +180 -0
- package/dist/src/search/IndexIdentityGuard.d.ts +11 -0
- package/dist/src/search/IndexIdentityGuard.js +37 -0
- package/dist/src/search/ProjectCatalog.d.ts +11 -0
- package/dist/src/search/ProjectCatalog.js +38 -0
- package/dist/src/search/SymbolLookup.d.ts +24 -0
- package/dist/src/search/SymbolLookup.js +97 -0
- package/dist/src/search/chunks/CandidateFetcher.d.ts +19 -0
- package/dist/src/search/chunks/CandidateFetcher.js +40 -0
- package/dist/src/search/chunks/IterativeScanGate.d.ts +10 -0
- package/dist/src/search/chunks/IterativeScanGate.js +42 -0
- package/dist/src/search/chunks/RerankStage.d.ts +15 -0
- package/dist/src/search/chunks/RerankStage.js +42 -0
- package/dist/src/search/chunks/search-metrics.d.ts +18 -0
- package/dist/src/search/chunks/search-metrics.js +32 -0
- package/dist/src/search/chunks/search-plan.d.ts +18 -0
- package/dist/src/search/chunks/search-plan.js +19 -0
- package/dist/src/search/create-search-services.d.ts +26 -0
- package/dist/src/search/create-search-services.js +29 -0
- package/dist/src/search/index-paths.d.ts +1 -0
- package/dist/src/search/index-paths.js +5 -0
- package/dist/src/search/symbol-sql.d.ts +13 -0
- package/dist/src/search/symbol-sql.js +66 -0
- package/dist/src/search/symbols/SymbolGrepFallback.d.ts +19 -0
- package/dist/src/search/symbols/SymbolGrepFallback.js +106 -0
- package/dist/src/search/symbols/SymbolIndexQuery.d.ts +14 -0
- package/dist/src/search/symbols/SymbolIndexQuery.js +52 -0
- package/dist/src/search/symbols/SymbolTextSearch.d.ts +13 -0
- package/dist/src/search/symbols/SymbolTextSearch.js +84 -0
- package/dist/src/search/symbols/TargetColumnGate.d.ts +7 -0
- package/dist/src/search/symbols/TargetColumnGate.js +23 -0
- package/dist/src/search/symbols/find-symbol-constants.d.ts +4 -0
- package/dist/src/search/symbols/find-symbol-constants.js +8 -0
- package/dist/src/search/symbols/grep-args.d.ts +3 -0
- package/dist/src/search/symbols/grep-args.js +7 -0
- package/dist/src/search/symbols/grep-patterns.d.ts +4 -0
- package/dist/src/search/symbols/grep-patterns.js +60 -0
- package/dist/src/services/AstParserService.d.ts +8 -44
- package/dist/src/services/AstParserService.js +19 -938
- package/dist/src/services/DatabaseService.d.ts +8 -10
- package/dist/src/services/DatabaseService.js +29 -86
- package/dist/src/services/IndexerService.d.ts +7 -10
- package/dist/src/services/IndexerService.js +36 -255
- package/dist/src/services/McpReadiness.d.ts +18 -0
- package/dist/src/services/McpReadiness.js +73 -0
- package/dist/src/services/McpServer.d.ts +32 -9
- package/dist/src/services/McpServer.js +78 -41
- package/dist/src/services/WebServerService.d.ts +4 -3
- package/dist/src/services/WebServerService.js +16 -8
- package/dist/src/setup.d.ts +16 -4
- package/dist/src/setup.js +115 -40
- package/dist/src/symbol-name.d.ts +1 -0
- package/dist/src/symbol-name.js +16 -12
- package/dist/src/tools/FindSymbolTool.d.ts +17 -7
- package/dist/src/tools/FindSymbolTool.js +54 -35
- package/dist/src/tools/GetCallersTool.d.ts +16 -8
- package/dist/src/tools/GetCallersTool.js +65 -35
- package/dist/src/tools/GetDependenciesTool.d.ts +16 -8
- package/dist/src/tools/GetDependenciesTool.js +35 -28
- package/dist/src/tools/GetDependentsTool.d.ts +20 -8
- package/dist/src/tools/GetDependentsTool.js +47 -33
- package/dist/src/tools/ListFilesTool.d.ts +16 -8
- package/dist/src/tools/ListFilesTool.js +20 -26
- package/dist/src/tools/ListProjectsTool.d.ts +11 -7
- package/dist/src/tools/ListProjectsTool.js +13 -11
- package/dist/src/tools/McpTool.d.ts +17 -0
- package/dist/src/tools/McpTool.js +28 -0
- package/dist/src/tools/ReadFileRangeTool.d.ts +16 -8
- package/dist/src/tools/ReadFileRangeTool.js +23 -25
- package/dist/src/tools/ReadFileTool.d.ts +15 -8
- package/dist/src/tools/ReadFileTool.js +19 -24
- package/dist/src/tools/SearchCodebaseTool.d.ts +22 -10
- package/dist/src/tools/SearchCodebaseTool.js +69 -51
- package/dist/src/tools/format-helpers.d.ts +9 -3
- package/dist/src/tools/format-helpers.js +59 -14
- package/dist/src/tools/tool-args.d.ts +38 -0
- package/dist/src/tools/tool-args.js +95 -0
- package/dist/src/workspace/GrepRunner.d.ts +15 -0
- package/dist/src/workspace/GrepRunner.js +104 -0
- package/dist/src/workspace/ProjectRoots.d.ts +22 -0
- package/dist/src/workspace/ProjectRoots.js +102 -0
- package/dist/src/workspace/ToolPaths.d.ts +31 -0
- package/dist/src/workspace/ToolPaths.js +312 -0
- package/dist/src/workspace/WorkspaceFiles.d.ts +19 -0
- package/dist/src/workspace/WorkspaceFiles.js +285 -0
- package/dist/src/workspace/fs-access.d.ts +9 -0
- package/dist/src/workspace/fs-access.js +47 -0
- package/dist/src/workspace/path-helpers.d.ts +3 -0
- package/dist/src/workspace/path-helpers.js +43 -0
- package/package.json +4 -3
- package/dist/src/interfaces/ISearcher.d.ts +0 -46
- package/dist/src/services/SearcherService.d.ts +0 -235
- package/dist/src/services/SearcherService.js +0 -1118
- /package/dist/src/{interfaces/ISearcher.js → db/drizzle-types.js} +0 -0
package/dist/src/setup.js
CHANGED
|
@@ -34,8 +34,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.EMBEDDING_PROVIDER_CHOICES = void 0;
|
|
37
|
+
exports.recordedStep = recordedStep;
|
|
38
|
+
exports.setupStepLogger = setupStepLogger;
|
|
37
39
|
exports.dockerPromptDefault = dockerPromptDefault;
|
|
38
40
|
exports.rerankerChoices = rerankerChoices;
|
|
41
|
+
exports.voyageEmbeddingModels = voyageEmbeddingModels;
|
|
42
|
+
exports.promptModelChoice = promptModelChoice;
|
|
43
|
+
exports.promptVoyageSetup = promptVoyageSetup;
|
|
44
|
+
exports.promptSearchThreshold = promptSearchThreshold;
|
|
45
|
+
exports.searchThresholdBlock = searchThresholdBlock;
|
|
39
46
|
exports.promptReranker = promptReranker;
|
|
40
47
|
exports.prepareComposeAndStartDb = prepareComposeAndStartDb;
|
|
41
48
|
exports.provisionInfinity = provisionInfinity;
|
|
@@ -57,6 +64,18 @@ const DOCKER_COMPOSE_SRC = path.join(_projectRoot, 'docker', 'docker-compose.yml
|
|
|
57
64
|
const DOCKER_COMPOSE_DST = path.join(config_store_1.CONFIG_DIR, 'docker-compose.yml');
|
|
58
65
|
const DOCKER_GPU_OVERRIDE_SRC = path.join(_projectRoot, 'docker', 'docker-compose.infinity-gpu.yml');
|
|
59
66
|
const DOCKER_GPU_OVERRIDE_DST = path.join(config_store_1.CONFIG_DIR, 'docker-compose.infinity-gpu.yml');
|
|
67
|
+
function recordedStep(question, raw) {
|
|
68
|
+
const q = question.split('\n').find(l => l.trim())?.trim() ?? question.trim();
|
|
69
|
+
const a = /password|key|secret|token/i.test(q) ? '***' : raw.trim();
|
|
70
|
+
return { q, a };
|
|
71
|
+
}
|
|
72
|
+
function setupStepLogger(logger, steps) {
|
|
73
|
+
return (question, raw) => {
|
|
74
|
+
const step = recordedStep(question, raw);
|
|
75
|
+
logger?.debug('setup: prompt', step);
|
|
76
|
+
steps.push(step);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
60
79
|
function ask(rl, question) {
|
|
61
80
|
return new Promise(resolve => rl.question(question, resolve));
|
|
62
81
|
}
|
|
@@ -71,7 +90,6 @@ function expandHome(p) {
|
|
|
71
90
|
}
|
|
72
91
|
const PROVIDER_DEFAULTS = {
|
|
73
92
|
'openai-compatible': { baseURL: 'https://api.openai.com/v1', model: 'text-embedding-3-small', envVar: 'OPENAI_API_KEY' },
|
|
74
|
-
'voyage': { baseURL: 'https://api.voyageai.com/v1', model: 'voyage-code-4', envVar: 'VOYAGE_API_KEY' },
|
|
75
93
|
'infinity': { baseURL: 'http://localhost:7997', model: 'jinaai/jina-embeddings-v2-base-code', envVar: '' },
|
|
76
94
|
};
|
|
77
95
|
exports.EMBEDDING_PROVIDER_CHOICES = ['infinity', 'openai-compatible', 'voyage'];
|
|
@@ -79,7 +97,7 @@ function isLocalBackend(p) {
|
|
|
79
97
|
return p === 'infinity';
|
|
80
98
|
}
|
|
81
99
|
async function checkEmbeddingBackend(provider, baseURL) {
|
|
82
|
-
if (!isLocalBackend(provider))
|
|
100
|
+
if (!isLocalBackend(provider) || !baseURL)
|
|
83
101
|
return true;
|
|
84
102
|
return (0, infinity_provisioning_1.probeInfinityHealth)(baseURL);
|
|
85
103
|
}
|
|
@@ -101,23 +119,78 @@ function writeEnvKey(name, value) {
|
|
|
101
119
|
}
|
|
102
120
|
catch { }
|
|
103
121
|
}
|
|
122
|
+
const VOYAGE_API_ENV = 'VOYAGE_API_KEY';
|
|
104
123
|
const VOYAGE_RERANK_ENV = 'VOYAGE_RERANK_API_KEY';
|
|
105
124
|
const RERANKER_ENV = 'RERANKER_API_KEY';
|
|
106
125
|
function rerankerChoices(embProvider) {
|
|
107
126
|
if (embProvider === 'infinity')
|
|
108
127
|
return ['infinity', 'voyage', 'openai-compatible', 'none'];
|
|
109
|
-
if (embProvider === 'voyage')
|
|
110
|
-
return ['voyage', 'openai-compatible', 'none'];
|
|
111
128
|
return ['none', 'voyage', 'openai-compatible'];
|
|
112
129
|
}
|
|
113
|
-
|
|
130
|
+
function voyageEmbeddingModels() {
|
|
131
|
+
return Object.entries(constants_1.MODEL_REGISTRY).filter(([, info]) => info.provider === 'voyage').map(([name]) => name);
|
|
132
|
+
}
|
|
133
|
+
async function askRequired(io, question, required = ' A value is required.') {
|
|
114
134
|
for (;;) {
|
|
115
135
|
const answer = (await io.ask(question)).trim();
|
|
116
136
|
if (answer)
|
|
117
137
|
return answer;
|
|
118
|
-
io.log(
|
|
138
|
+
io.log(required);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function promptModelChoice(io, label, models) {
|
|
142
|
+
models.forEach((m, i) => io.log(` ${i + 1}) ${m}${i === 0 ? ' (default)' : ''}`));
|
|
143
|
+
for (;;) {
|
|
144
|
+
const answer = (await io.ask(`${label} (1-${models.length} or a model name) (1): `)).trim();
|
|
145
|
+
if (!answer)
|
|
146
|
+
return models[0];
|
|
147
|
+
if (!/^\d+$/.test(answer))
|
|
148
|
+
return answer;
|
|
149
|
+
const n = parseInt(answer, 10);
|
|
150
|
+
if (n >= 1 && n <= models.length)
|
|
151
|
+
return models[n - 1];
|
|
152
|
+
io.log(` Enter a number from 1-${models.length}, or a model name.`);
|
|
119
153
|
}
|
|
120
154
|
}
|
|
155
|
+
async function promptVoyageSetup(io) {
|
|
156
|
+
const key = await askRequired(io, `Voyage API key (stored in ${config_store_1.ENV_FILE} as ${VOYAGE_API_ENV}): `, ' The Voyage API key is required.');
|
|
157
|
+
io.writeEnvKey(VOYAGE_API_ENV, key);
|
|
158
|
+
io.log(` Saved API key to ${config_store_1.ENV_FILE} as ${VOYAGE_API_ENV}.`);
|
|
159
|
+
const apiKey = `env:${VOYAGE_API_ENV}`;
|
|
160
|
+
const model = await promptModelChoice(io, 'Embedding model', voyageEmbeddingModels());
|
|
161
|
+
const embedding = { provider: 'voyage', model, apiKey };
|
|
162
|
+
io.log('\n--- Reranker ---');
|
|
163
|
+
let enable;
|
|
164
|
+
for (;;) {
|
|
165
|
+
enable = (await io.prompt('Enable Voyage reranker? (y/n)', 'y')).trim().toLowerCase();
|
|
166
|
+
if (enable === 'y' || enable === 'n')
|
|
167
|
+
break;
|
|
168
|
+
io.log(' Answer y or n.');
|
|
169
|
+
}
|
|
170
|
+
if (enable === 'n')
|
|
171
|
+
return { embedding, reranker: { enabled: false } };
|
|
172
|
+
const rerankModel = await promptModelChoice(io, 'Reranker model', constants_1.VOYAGE_RERANK_MODELS);
|
|
173
|
+
return { embedding, reranker: { enabled: true, provider: 'voyage', model: rerankModel, apiKey } };
|
|
174
|
+
}
|
|
175
|
+
async function promptSearchThreshold(io, model) {
|
|
176
|
+
const recommended = constants_1.MODEL_REGISTRY[model]?.defaultThreshold;
|
|
177
|
+
io.log('\n--- Search threshold ---');
|
|
178
|
+
io.log(recommended !== undefined
|
|
179
|
+
? ` Recommended for ${model}: ${(0, constants_1.formatThreshold)(recommended)}`
|
|
180
|
+
: ` ${model} has no recommended value; left blank, only unrelated results are filtered out.`);
|
|
181
|
+
for (;;) {
|
|
182
|
+
const answer = (await io.ask('Similarity threshold: max cosine distance 0-1, lower is stricter (blank to follow the model): ')).trim();
|
|
183
|
+
if (!answer)
|
|
184
|
+
return undefined;
|
|
185
|
+
const value = Number(answer);
|
|
186
|
+
if (value >= 0 && value <= 1)
|
|
187
|
+
return value;
|
|
188
|
+
io.log(' Enter a number from 0 to 1, or leave it blank.');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function searchThresholdBlock(threshold) {
|
|
192
|
+
return threshold === undefined ? {} : { search: { similarityThreshold: threshold } };
|
|
193
|
+
}
|
|
121
194
|
function isHttpUrl(value) {
|
|
122
195
|
try {
|
|
123
196
|
const { protocol, username, password } = new URL(value);
|
|
@@ -181,6 +254,31 @@ async function promptReranker(embProvider, embBaseURL, io) {
|
|
|
181
254
|
...(key ? { apiKey: `env:${RERANKER_ENV}` } : {}),
|
|
182
255
|
};
|
|
183
256
|
}
|
|
257
|
+
async function promptGenericBackend(embProvider, io) {
|
|
258
|
+
const pd = PROVIDER_DEFAULTS[embProvider];
|
|
259
|
+
const embBaseURL = await io.prompt('Base URL', pd.baseURL);
|
|
260
|
+
const embModel = await io.prompt('Embedding model', pd.model);
|
|
261
|
+
let apiKeyRef = '';
|
|
262
|
+
if (!isLocalBackend(embProvider)) {
|
|
263
|
+
const key = (await io.ask(`API key (stored in ${config_store_1.ENV_FILE}, leave blank to set later): `)).trim();
|
|
264
|
+
apiKeyRef = `env:${pd.envVar}`;
|
|
265
|
+
if (key) {
|
|
266
|
+
io.writeEnvKey(pd.envVar, key);
|
|
267
|
+
io.log(` Saved API key to ${config_store_1.ENV_FILE} as ${pd.envVar} (config references env:${pd.envVar}).`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
io.log(` No key entered — set it later: echo '${pd.envVar}=sk-…' >> ${config_store_1.ENV_FILE}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const reranker = await promptReranker(embProvider, embBaseURL, io);
|
|
274
|
+
const embedding = {
|
|
275
|
+
provider: embProvider,
|
|
276
|
+
baseURL: embBaseURL,
|
|
277
|
+
model: embModel,
|
|
278
|
+
...(apiKeyRef ? { apiKey: apiKeyRef } : {}),
|
|
279
|
+
};
|
|
280
|
+
return { embedding, reranker };
|
|
281
|
+
}
|
|
184
282
|
async function waitForDb(host, port, user, pass, db, maxWaitSec = 60, logger, steps) {
|
|
185
283
|
const { Client } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
186
284
|
const deadline = Date.now() + maxWaitSec * 1000;
|
|
@@ -309,15 +407,10 @@ async function provisionInfinity(savedConfig, startDocker, logger, deps = {}, co
|
|
|
309
407
|
async function runSetup(logger) {
|
|
310
408
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
311
409
|
const steps = [];
|
|
312
|
-
|
|
313
|
-
const a = /password|key|secret|token/i.test(q) ? '***' : raw.trim();
|
|
314
|
-
logger?.debug('setup: prompt', { q, a });
|
|
315
|
-
steps.push({ q, a });
|
|
316
|
-
}
|
|
410
|
+
const logStep = setupStepLogger(logger, steps);
|
|
317
411
|
const loggedAsk = async (question) => {
|
|
318
412
|
const answer = await ask(rl, question);
|
|
319
|
-
|
|
320
|
-
logStep(key, answer);
|
|
413
|
+
logStep(question, answer);
|
|
321
414
|
return answer;
|
|
322
415
|
};
|
|
323
416
|
const loggedPrompt = async (label, def) => {
|
|
@@ -351,25 +444,11 @@ async function runSetup(logger) {
|
|
|
351
444
|
if (!exports.EMBEDDING_PROVIDER_CHOICES.includes(embProvider))
|
|
352
445
|
console.log(` Invalid provider. Choose: ${exports.EMBEDDING_PROVIDER_CHOICES.join(', ')}`);
|
|
353
446
|
} while (!exports.EMBEDDING_PROVIDER_CHOICES.includes(embProvider));
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const key = (await loggedAsk(`API key (stored in ${config_store_1.ENV_FILE}, leave blank to set later): `)).trim();
|
|
360
|
-
if (key) {
|
|
361
|
-
writeEnvKey(pd.envVar, key);
|
|
362
|
-
apiKeyRef = `env:${pd.envVar}`;
|
|
363
|
-
console.log(` Saved API key to ${config_store_1.ENV_FILE} as ${pd.envVar} (config references env:${pd.envVar}).`);
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
apiKeyRef = `env:${pd.envVar}`;
|
|
367
|
-
console.log(` No key entered — set it later: echo '${pd.envVar}=sk-…' >> ${config_store_1.ENV_FILE}`);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
const reranker = await promptReranker(embProvider, embBaseURL, {
|
|
371
|
-
prompt: loggedPrompt, ask: loggedAsk, writeEnvKey, log: msg => console.log(msg),
|
|
372
|
-
});
|
|
447
|
+
const promptIO = { prompt: loggedPrompt, ask: loggedAsk, writeEnvKey, log: msg => console.log(msg) };
|
|
448
|
+
const { embedding, reranker } = embProvider === 'voyage'
|
|
449
|
+
? await promptVoyageSetup(promptIO)
|
|
450
|
+
: await promptGenericBackend(embProvider, promptIO);
|
|
451
|
+
const searchThreshold = await promptSearchThreshold(promptIO, embedding.model ?? config_store_1.CONFIG_DEFAULTS['embedding.model']);
|
|
373
452
|
console.log('\n--- Database ---');
|
|
374
453
|
const dbHost = await loggedPrompt('DB host', 'localhost');
|
|
375
454
|
const dbPort = await loggedPrompt('DB port', '5433');
|
|
@@ -380,7 +459,7 @@ async function runSetup(logger) {
|
|
|
380
459
|
if (dbReachable)
|
|
381
460
|
console.log(' Database is already reachable.');
|
|
382
461
|
process.stdout.write('Checking embedding backend...');
|
|
383
|
-
const embReachable = await checkEmbeddingBackend(embProvider,
|
|
462
|
+
const embReachable = await checkEmbeddingBackend(embProvider, embedding.baseURL);
|
|
384
463
|
if (isLocalBackend(embProvider)) {
|
|
385
464
|
console.log(embReachable ? ` ${embProvider} is already reachable.` : ` ${embProvider} is not reachable — it can be started via Docker.`);
|
|
386
465
|
}
|
|
@@ -432,13 +511,9 @@ async function runSetup(logger) {
|
|
|
432
511
|
user: dbVars.user,
|
|
433
512
|
password: dbVars.password,
|
|
434
513
|
},
|
|
435
|
-
embedding
|
|
436
|
-
provider: embProvider,
|
|
437
|
-
baseURL: embBaseURL,
|
|
438
|
-
model: embModel,
|
|
439
|
-
...(apiKeyRef ? { apiKey: apiKeyRef } : {}),
|
|
440
|
-
},
|
|
514
|
+
embedding,
|
|
441
515
|
reranker,
|
|
516
|
+
...searchThresholdBlock(searchThreshold),
|
|
442
517
|
logging: { level: logLevel },
|
|
443
518
|
workspaceRoot,
|
|
444
519
|
};
|
|
@@ -452,7 +527,7 @@ async function runSetup(logger) {
|
|
|
452
527
|
}
|
|
453
528
|
console.log('\nInitializing database schema...');
|
|
454
529
|
const { DatabaseService } = await Promise.resolve().then(() => __importStar(require('./services/DatabaseService')));
|
|
455
|
-
const dbService = new DatabaseService(new config_1.Config());
|
|
530
|
+
const dbService = new DatabaseService(new config_1.Config(), logger);
|
|
456
531
|
let initError = null;
|
|
457
532
|
try {
|
|
458
533
|
await dbService.initDbWithSchema();
|
package/dist/src/symbol-name.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QUALIFIED_NAME_RE = exports.BARE_NAME_RE = void 0;
|
|
3
|
+
exports.MAX_GREPPED_NAME_LEN = exports.QUALIFIED_NAME_RE = exports.BARE_NAME_RE = void 0;
|
|
4
4
|
exports.splitQualified = splitQualified;
|
|
5
5
|
exports.parseFindSymbolName = parseFindSymbolName;
|
|
6
6
|
exports.parseCallerSymbol = parseCallerSymbol;
|
|
@@ -10,7 +10,7 @@ exports.QUALIFIED_NAME_RE = /^#?[\w$]+(\.#?[\w$]+)+$/;
|
|
|
10
10
|
const PFX_SRC = '(?:#?[\\w$]+(?:\\.#?[\\w$]+)*\\.)?';
|
|
11
11
|
const QUOTED_RE = new RegExp(`^(${PFX_SRC})('[^'\\r\\n]+'|"[^"\\r\\n]+")$`);
|
|
12
12
|
const COMPUTED_RE = new RegExp(`^(${PFX_SRC})(\\[[^\\]\\r\\n]+\\])$`);
|
|
13
|
-
|
|
13
|
+
exports.MAX_GREPPED_NAME_LEN = 256;
|
|
14
14
|
function stripTrailingDot(prefix) {
|
|
15
15
|
if (!prefix)
|
|
16
16
|
return null;
|
|
@@ -21,15 +21,15 @@ function splitQualified(input) {
|
|
|
21
21
|
return { container: input.slice(0, idx), name: input.slice(idx + 1) };
|
|
22
22
|
}
|
|
23
23
|
function parseFindSymbolName(input) {
|
|
24
|
-
if (input.length
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
24
|
+
if (input.length > exports.MAX_GREPPED_NAME_LEN)
|
|
25
|
+
return null;
|
|
26
|
+
const quoted = input.match(QUOTED_RE);
|
|
27
|
+
if (quoted) {
|
|
28
|
+
return { form: 'quoted', container: stripTrailingDot(quoted[1]), name: quoted[2].slice(1, -1) };
|
|
29
|
+
}
|
|
30
|
+
const computed = input.match(COMPUTED_RE);
|
|
31
|
+
if (computed) {
|
|
32
|
+
return { form: 'computed', container: stripTrailingDot(computed[1]), name: computed[2].replace(/\s+/g, '') };
|
|
33
33
|
}
|
|
34
34
|
if (exports.QUALIFIED_NAME_RE.test(input)) {
|
|
35
35
|
const { container, name } = splitQualified(input);
|
|
@@ -40,8 +40,12 @@ function parseFindSymbolName(input) {
|
|
|
40
40
|
}
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
|
+
function isQualifiedName(input) {
|
|
44
|
+
const segments = input.split('.');
|
|
45
|
+
return segments.length >= 2 && segments.every(segment => exports.BARE_NAME_RE.test(segment));
|
|
46
|
+
}
|
|
43
47
|
function parseCallerSymbol(input) {
|
|
44
|
-
if (
|
|
48
|
+
if (isQualifiedName(input)) {
|
|
45
49
|
const { container, name } = splitQualified(input);
|
|
46
50
|
return { form: 'qualified', container, name };
|
|
47
51
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
3
|
+
import type { ISymbolLookup } from '../interfaces/ISymbolLookup';
|
|
4
|
+
import type { SymbolKind } from '../interfaces/ISymbolLookup';
|
|
5
|
+
import type { ILogger } from '../interfaces/ILogger';
|
|
6
|
+
import { McpTool } from './McpTool';
|
|
7
|
+
declare const ARGS: z.ZodObject<{
|
|
8
|
+
name: z.ZodType<string, unknown, z.core.$ZodTypeInternals<string, unknown>>;
|
|
9
|
+
project: z.ZodType<string | undefined, unknown, z.core.$ZodTypeInternals<string | undefined, unknown>>;
|
|
10
|
+
kind: z.ZodType<SymbolKind, unknown, z.core.$ZodTypeInternals<SymbolKind, unknown>>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export declare class FindSymbolTool extends McpTool<typeof ARGS> {
|
|
13
|
+
private symbols;
|
|
14
|
+
constructor(symbols: ISymbolLookup, logger?: ILogger);
|
|
15
|
+
protected run({ name, project, kind }: z.output<typeof ARGS>): Promise<ToolResponse>;
|
|
16
|
+
private emptyText;
|
|
8
17
|
}
|
|
18
|
+
export {};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FindSymbolTool = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const LoggerService_1 = require("../services/LoggerService");
|
|
4
6
|
const format_helpers_1 = require("./format-helpers");
|
|
7
|
+
const McpTool_1 = require("./McpTool");
|
|
8
|
+
const tool_args_1 = require("./tool-args");
|
|
5
9
|
const VALID_KINDS = [
|
|
6
10
|
'function', 'class', 'type', 'interface', 'variable',
|
|
7
11
|
'method', 'get_accessor', 'set_accessor', 'constructor', 'function_property', 'method_signature',
|
|
@@ -13,13 +17,17 @@ const GRAPHQL_KINDS = new Set([
|
|
|
13
17
|
]);
|
|
14
18
|
const GRAPHQL_STALE_INDEX_HINT = 'Files indexed before GraphQL indexing was added have no GraphQL rows until `rag-index --reindex` — ' +
|
|
15
19
|
'this miss does not prove the name is absent.';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const GREP_ADVICE = 'The index may be behind the source, so check with Grep before concluding the symbol does not exist.';
|
|
21
|
+
const PARTIAL_NOTE = '*(partial — some files could not be searched)*';
|
|
22
|
+
const ARGS = zod_1.z.object({
|
|
23
|
+
name: (0, tool_args_1.requiredText)('Bare, qualified (Class.method), quoted, or computed symbol name (case-sensitive)'),
|
|
24
|
+
project: (0, tool_args_1.optionalText)('Limit search to this project (optional). Use list_projects to find names.'),
|
|
25
|
+
kind: (0, tool_args_1.oneOf)('Symbol kind filter. Default: all.', VALID_KINDS, 'all'),
|
|
26
|
+
});
|
|
27
|
+
class FindSymbolTool extends McpTool_1.McpTool {
|
|
28
|
+
symbols;
|
|
29
|
+
constructor(symbols, logger = LoggerService_1.LoggerService.silent()) {
|
|
30
|
+
super({
|
|
23
31
|
name: 'find_symbol',
|
|
24
32
|
description: 'Find TypeScript/JavaScript symbol definitions by exact name: top-level declarations (functions, classes, types, ' +
|
|
25
33
|
'interfaces, variables), class/object-literal members (methods, get/set accessors, constructors, function ' +
|
|
@@ -30,48 +38,59 @@ class FindSymbolTool {
|
|
|
30
38
|
'resolve field (`@ResolveField`) or type (`@ObjectType`/`@InputType`/`@InterfaceType`) by its GraphQL name — the ' +
|
|
31
39
|
'name the schema exposes, which may differ from the TypeScript declaration name. Pass the matching `graphql_*` ' +
|
|
32
40
|
'kind when the name comes from a schema, a GraphQL document or a frontend.',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
name: { type: 'string', description: 'Bare, qualified (Class.method), quoted, or computed symbol name (case-sensitive)' },
|
|
37
|
-
project: { type: 'string', description: 'Limit search to this project (optional). Use list_projects to find names.' },
|
|
38
|
-
kind: { type: 'string', enum: VALID_KINDS, description: 'Symbol kind filter. Default: all.' },
|
|
39
|
-
},
|
|
40
|
-
required: ['name'],
|
|
41
|
-
},
|
|
42
|
-
};
|
|
41
|
+
args: ARGS,
|
|
42
|
+
}, logger);
|
|
43
|
+
this.symbols = symbols;
|
|
43
44
|
}
|
|
44
|
-
async
|
|
45
|
-
const name = String(args.name ?? '');
|
|
46
|
-
const project = args.project ? String(args.project) : null;
|
|
47
|
-
const rawKind = args.kind;
|
|
48
|
-
const kind = VALID_KINDS.includes(rawKind) ? rawKind : 'all';
|
|
45
|
+
async run({ name, project, kind }) {
|
|
49
46
|
let parsed;
|
|
50
47
|
try {
|
|
51
|
-
parsed = await this.
|
|
48
|
+
parsed = await this.symbols.findSymbol(name, project ?? null, kind);
|
|
52
49
|
}
|
|
53
50
|
catch (err) {
|
|
54
51
|
return (0, format_helpers_1.errorResponse)(err.message);
|
|
55
52
|
}
|
|
56
|
-
const { hits, truncated,
|
|
53
|
+
const { hits, truncated, grep } = parsed;
|
|
54
|
+
if (hits.length === 0)
|
|
55
|
+
return (0, format_helpers_1.textResponse)(this.emptyText(name, project ?? null, kind, parsed).join('\n'));
|
|
57
56
|
const lines = [];
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
const notes = [
|
|
58
|
+
...(truncated ? [`*(truncated to ${hits.length} results)*`] : []),
|
|
59
|
+
...(grep.outcome === 'partial' ? [PARTIAL_NOTE] : []),
|
|
60
|
+
];
|
|
61
|
+
if (notes.length > 0)
|
|
62
|
+
lines.push(`${notes.join(' ')}\n`);
|
|
63
|
+
const grepTag = name.includes('.')
|
|
64
|
+
? ' (grep — not in index; container not checked; may be stale or imprecise)'
|
|
65
|
+
: ' (grep — not in index; may be stale or imprecise)';
|
|
67
66
|
for (const h of hits) {
|
|
68
67
|
const staticTag = h.isStatic ? ' static' : '';
|
|
69
68
|
const targetTag = h.target ? ` → \`${h.target}\`` : '';
|
|
70
|
-
const
|
|
71
|
-
lines.push(`**${h.kind}** \`${h.qualified}\`${staticTag}${targetTag} \`${h.filePath}\` line ${h.line}, col ${h.column}${
|
|
69
|
+
const sourceTag = h.source === 'grep' ? grepTag : '';
|
|
70
|
+
lines.push(`**${h.kind}** \`${h.qualified}\`${staticTag}${targetTag} \`${h.filePath}\` line ${h.line}, col ${h.column}${sourceTag}`);
|
|
72
71
|
lines.push(`\`\`\`\n${h.signature}\n\`\`\``);
|
|
73
72
|
}
|
|
74
73
|
return (0, format_helpers_1.textResponse)(lines.join('\n'));
|
|
75
74
|
}
|
|
75
|
+
emptyText(name, project, kind, { grep, similar }) {
|
|
76
|
+
const suffix = grep.outcome === 'failed' ? ` (grep fallback failed: ${grep.cause})` : '';
|
|
77
|
+
const lines = [`No definitions found for symbol: ${name}${suffix}`];
|
|
78
|
+
if (GRAPHQL_KINDS.has(kind))
|
|
79
|
+
return [...lines, GRAPHQL_STALE_INDEX_HINT];
|
|
80
|
+
if (grep.outcome === 'no-project-directory') {
|
|
81
|
+
lines.push(`Project \`${project}\` has no directory in the workspace, so source files were not searched. Check the name with \`list_projects\`.`);
|
|
82
|
+
}
|
|
83
|
+
if (grep.outcome === 'other-container') {
|
|
84
|
+
const dot = name.lastIndexOf('.');
|
|
85
|
+
lines.push(`\`${name.slice(dot + 1)}\` is indexed, but not under \`${name.slice(0, dot)}\`, so source files were not searched.`);
|
|
86
|
+
}
|
|
87
|
+
if (similar.names.length > 0) {
|
|
88
|
+
lines.push('Similar indexed names:', ...similar.names.map(n => ` → \`${n}\``));
|
|
89
|
+
if (similar.truncated)
|
|
90
|
+
lines.push(`*(truncated to ${similar.names.length} results)*`);
|
|
91
|
+
lines.push(GREP_ADVICE);
|
|
92
|
+
}
|
|
93
|
+
return lines;
|
|
94
|
+
}
|
|
76
95
|
}
|
|
77
96
|
exports.FindSymbolTool = FindSymbolTool;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
3
|
+
import type { IToolPaths } from '../interfaces/IToolPaths';
|
|
4
|
+
import type { ICallGraph } from '../interfaces/ICallGraph';
|
|
3
5
|
import type { ILogger } from '../interfaces/ILogger';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { McpTool } from './McpTool';
|
|
7
|
+
declare const ARGS: z.ZodObject<{
|
|
8
|
+
symbol: z.ZodType<string, unknown, z.core.$ZodTypeInternals<string, unknown>>;
|
|
9
|
+
project: z.ZodType<string, unknown, z.core.$ZodTypeInternals<string, unknown>>;
|
|
10
|
+
callee_file: z.ZodType<string | undefined, unknown, z.core.$ZodTypeInternals<string | undefined, unknown>>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export declare class GetCallersTool extends McpTool<typeof ARGS> {
|
|
13
|
+
private paths;
|
|
14
|
+
private calls;
|
|
15
|
+
constructor(paths: IToolPaths, calls: ICallGraph, logger: ILogger);
|
|
16
|
+
protected run({ symbol, project, callee_file: input }: z.output<typeof ARGS>): Promise<ToolResponse>;
|
|
10
17
|
}
|
|
18
|
+
export {};
|
|
@@ -1,59 +1,89 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GetCallersTool = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
4
5
|
const format_helpers_1 = require("./format-helpers");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const McpTool_1 = require("./McpTool");
|
|
7
|
+
const tool_args_1 = require("./tool-args");
|
|
8
|
+
const symbol_name_1 = require("../symbol-name");
|
|
9
|
+
function truncationMarker(shown, truncated) {
|
|
10
|
+
return truncated ? [`*(truncated to ${shown} results)*`] : [];
|
|
11
|
+
}
|
|
12
|
+
function renderCause(project, calleeFile, cause) {
|
|
13
|
+
switch (cause.kind) {
|
|
14
|
+
case 'no-call-graph':
|
|
15
|
+
return [`Project \`${project}\` has no call graph. Check the name with \`list_projects\`. If the project is not indexed yet, run \`rag-index --project ${project}\`; an indexed project without internal calls has no graph to show.`];
|
|
16
|
+
case 'unknown-symbol':
|
|
17
|
+
return [
|
|
18
|
+
'No such symbol is indexed in this project.',
|
|
19
|
+
...(cause.suggestions.length > 0 ? ['Similar indexed names:', ...cause.suggestions.map(q => ` → \`${q}\``)] : []),
|
|
20
|
+
...truncationMarker(cause.suggestions.length, cause.truncated),
|
|
21
|
+
'Look a name up with `find_symbol`. The index may be behind the source, so check with Grep before concluding the symbol does not exist.',
|
|
22
|
+
];
|
|
23
|
+
case 'no-recorded-calls': {
|
|
24
|
+
const recorded = calleeFile ? `No calls to \`${calleeFile}\` are recorded in this project` : 'No calls are recorded in this project';
|
|
25
|
+
const lastReasons = calleeFile ? 'or it is passed as a callback' : 'it is passed as a callback, or it is unused';
|
|
26
|
+
return [
|
|
27
|
+
'Defined at:',
|
|
28
|
+
...cause.definitions.map(d => ` → \`${d.qualified}\` \`${d.filePath}\`${d.line != null ? ` line ${d.line}` : ''}`),
|
|
29
|
+
...truncationMarker(cause.definitions.length, cause.truncated),
|
|
30
|
+
`${recorded}, because the call graph holds direct calls only, per calling project. ` +
|
|
31
|
+
'Possibly it is called from another project, a framework invokes it (decorators, dependency injection), ' +
|
|
32
|
+
'a call goes through an interface-typed receiver and is recorded under the interface member, ' +
|
|
33
|
+
`it is instantiated with \`new\`, ${lastReasons}.`,
|
|
34
|
+
'Find importers with `get_dependents` and retry `get_callers` with the project that owns an importer\'s path, as `list_projects` shows, or use Grep.',
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
case 'lookup-failed':
|
|
38
|
+
return ['The cause could not be determined.'];
|
|
11
39
|
}
|
|
12
|
-
|
|
13
|
-
|
|
40
|
+
}
|
|
41
|
+
function renderNoCallers(symbol, project, calleeFile, cause) {
|
|
42
|
+
return [
|
|
43
|
+
`No call sites found for \`${symbol}\` in project \`${project}\`.`,
|
|
44
|
+
...renderCause(project, calleeFile, cause),
|
|
45
|
+
...(calleeFile ? ['Retry without `callee_file` to match calls to any definition.'] : []),
|
|
46
|
+
].join('\n');
|
|
47
|
+
}
|
|
48
|
+
const ARGS = zod_1.z.object({
|
|
49
|
+
symbol: (0, tool_args_1.requiredText)('Bare or qualified (Class.method) function/method name (case-sensitive)'),
|
|
50
|
+
project: (0, tool_args_1.requiredText)('Project name (from list_projects)'),
|
|
51
|
+
callee_file: (0, tool_args_1.optionalPathArg)(`Optional: only calls to the definition in this file, to disambiguate overloaded names. ${(0, format_helpers_1.pathArgDescription)()}`),
|
|
52
|
+
});
|
|
53
|
+
class GetCallersTool extends McpTool_1.McpTool {
|
|
54
|
+
paths;
|
|
55
|
+
calls;
|
|
56
|
+
constructor(paths, calls, logger) {
|
|
57
|
+
super({
|
|
14
58
|
name: 'get_callers',
|
|
15
59
|
description: 'Find all call sites of a function or method by exact name. Accepts a bare name (every call site with that ' +
|
|
16
60
|
'callee name) or a qualified name (`Class.method`, from find_symbol output) to disambiguate same-named methods ' +
|
|
17
61
|
'of different classes. Uses the call graph index — faster and more precise than grep. A call through an ' +
|
|
18
62
|
'interface-typed receiver is recorded under the interface member (e.g. `IEmbedder.embedBatch`), not the ' +
|
|
19
63
|
'implementing class. Use after find_symbol to trace where a symbol is actually called.',
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
project: { type: 'string', description: 'Project name (from list_projects)' },
|
|
25
|
-
callee_file: { type: 'string', description: `Optional: only calls to the definition in this file, to disambiguate overloaded names. ${(0, format_helpers_1.pathArgDescription)()}` },
|
|
26
|
-
},
|
|
27
|
-
required: ['symbol', 'project'],
|
|
28
|
-
},
|
|
29
|
-
};
|
|
64
|
+
args: ARGS,
|
|
65
|
+
}, logger);
|
|
66
|
+
this.paths = paths;
|
|
67
|
+
this.calls = calls;
|
|
30
68
|
}
|
|
31
|
-
async
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const rawCallee = args.callee_file;
|
|
69
|
+
async run({ symbol, project, callee_file: input }) {
|
|
70
|
+
if (!(0, symbol_name_1.parseCallerSymbol)(symbol))
|
|
71
|
+
return (0, format_helpers_1.errorResponse)('Invalid symbol name');
|
|
35
72
|
let calleeFile = null;
|
|
36
|
-
if (
|
|
37
|
-
const
|
|
38
|
-
const resolved = await this.searcher.resolveToolPath(input);
|
|
73
|
+
if (input !== undefined) {
|
|
74
|
+
const resolved = await this.paths.resolveToolPath(input);
|
|
39
75
|
if (!resolved.ok)
|
|
40
76
|
return (0, format_helpers_1.pathErrorResponse)(this.logger, 'get_callers', 'callee_file', input, input, resolved);
|
|
41
|
-
const failure = await this.
|
|
77
|
+
const failure = await this.paths.checkGraphPath(resolved.relPath);
|
|
42
78
|
if (failure)
|
|
43
79
|
return (0, format_helpers_1.pathErrorResponse)(this.logger, 'get_callers', 'callee_file', input, resolved.relPath, failure);
|
|
44
80
|
calleeFile = resolved.relPath;
|
|
45
81
|
}
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
rows = await this.searcher.getCallers(symbol, project, calleeFile);
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
return (0, format_helpers_1.errorResponse)(err.message);
|
|
52
|
-
}
|
|
82
|
+
const rows = await this.calls.getCallers(symbol, project, calleeFile);
|
|
53
83
|
const lines = [];
|
|
54
84
|
if (rows.length === 0) {
|
|
55
|
-
|
|
56
|
-
return (0, format_helpers_1.textResponse)(
|
|
85
|
+
const cause = await this.calls.explainNoCallers(symbol, project);
|
|
86
|
+
return (0, format_helpers_1.textResponse)(renderNoCallers(symbol, project, calleeFile, cause));
|
|
57
87
|
}
|
|
58
88
|
lines.push(`**Call sites of \`${symbol}\`** (${rows.length} found)\n`);
|
|
59
89
|
for (const r of rows) {
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
3
|
+
import type { IToolPaths } from '../interfaces/IToolPaths';
|
|
4
|
+
import type { IDependencyGraph } from '../interfaces/IDependencyGraph';
|
|
3
5
|
import type { ILogger } from '../interfaces/ILogger';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { McpTool } from './McpTool';
|
|
7
|
+
declare const ARGS: z.ZodObject<{
|
|
8
|
+
file_path: z.ZodType<string, unknown, z.core.$ZodTypeInternals<string, unknown>>;
|
|
9
|
+
symbol: z.ZodType<string | undefined, unknown, z.core.$ZodTypeInternals<string | undefined, unknown>>;
|
|
10
|
+
transitive: z.ZodType<boolean | undefined, unknown, z.core.$ZodTypeInternals<boolean | undefined, unknown>>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export declare class GetDependenciesTool extends McpTool<typeof ARGS> {
|
|
13
|
+
private paths;
|
|
14
|
+
private dependencies;
|
|
15
|
+
constructor(paths: IToolPaths, dependencies: IDependencyGraph, logger: ILogger);
|
|
16
|
+
protected run(args: z.output<typeof ARGS>): Promise<ToolResponse>;
|
|
10
17
|
}
|
|
18
|
+
export {};
|