codesearch-mcp 0.1.0-alpha → 0.2.1-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 +39 -0
- package/README.md +146 -41
- package/dist/app.js +4 -19
- package/dist/src/commands/IndexCommand.js +68 -7
- package/dist/src/config-error.d.ts +7 -0
- package/dist/src/config-error.js +14 -0
- package/dist/src/config-store.d.ts +30 -12
- package/dist/src/config-store.js +81 -26
- package/dist/src/config.d.ts +5 -4
- package/dist/src/config.js +14 -16
- package/dist/src/constants.d.ts +8 -2
- package/dist/src/constants.js +8 -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 +75 -0
- package/dist/src/db/schema.js +10 -1
- package/dist/src/enriched-prompt.d.ts +8 -0
- package/dist/src/enriched-prompt.js +87 -0
- package/dist/src/help.js +1 -1
- package/dist/src/interfaces/IAstParser.d.ts +18 -2
- package/dist/src/interfaces/IDatabase.d.ts +31 -0
- package/dist/src/interfaces/ILogger.d.ts +7 -0
- package/dist/src/interfaces/ISearcher.d.ts +3 -0
- package/dist/src/services/AstChunker.d.ts +11 -0
- package/dist/src/services/AstChunker.js +44 -0
- package/dist/src/services/AstParserService.d.ts +39 -7
- package/dist/src/services/AstParserService.js +504 -104
- package/dist/src/services/DatabaseService.d.ts +17 -0
- package/dist/src/services/DatabaseService.js +130 -2
- package/dist/src/services/EmbedderFactory.d.ts +1 -1
- package/dist/src/services/EmbedderFactory.js +2 -5
- package/dist/src/services/IndexerService.d.ts +4 -14
- package/dist/src/services/IndexerService.js +54 -156
- package/dist/src/services/InfinityEmbedder.d.ts +14 -55
- package/dist/src/services/InfinityEmbedder.js +30 -141
- package/dist/src/services/LoggerService.d.ts +3 -1
- package/dist/src/services/LoggerService.js +17 -3
- package/dist/src/services/McpServer.js +3 -3
- package/dist/src/services/SearcherService.d.ts +39 -4
- package/dist/src/services/SearcherService.js +254 -81
- package/dist/src/services/WebServerService.d.ts +41 -3
- package/dist/src/services/WebServerService.js +90 -2
- package/dist/src/services/infinity-provisioning.d.ts +115 -0
- package/dist/src/services/infinity-provisioning.js +516 -0
- package/dist/src/services/legacy-ollama-index.d.ts +24 -0
- package/dist/src/services/legacy-ollama-index.js +61 -0
- package/dist/src/services/reranker.d.ts +3 -3
- package/dist/src/services/reranker.js +3 -3
- package/dist/src/setup.d.ts +37 -0
- package/dist/src/setup.js +111 -157
- package/dist/src/symbol-name.d.ts +44 -0
- package/dist/src/symbol-name.js +68 -0
- package/dist/src/symbol-rows.d.ts +12 -0
- package/dist/src/symbol-rows.js +38 -0
- package/dist/src/tools/FindSymbolTool.js +30 -7
- package/dist/src/tools/GetCallersTool.js +20 -5
- package/docker/docker-compose.yml +5 -15
- package/docs/architecture.svg +103 -0
- package/docs/web-dashboard.md +36 -17
- package/package.json +1 -6
- package/dist/src/commands/BackfillCommand.d.ts +0 -7
- package/dist/src/commands/BackfillCommand.js +0 -37
- package/dist/src/services/OllamaEmbedder.d.ts +0 -25
- package/dist/src/services/OllamaEmbedder.js +0 -111
|
@@ -33,167 +33,56 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.InfinityEmbedder = void 0;
|
|
37
|
-
exports.serverRoot = serverRoot;
|
|
38
|
-
exports.probeInfinityHealth = probeInfinityHealth;
|
|
39
|
-
exports.listInfinityModels = listInfinityModels;
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
41
|
-
const path = __importStar(require("path"));
|
|
42
|
-
const child_process_1 = require("child_process");
|
|
36
|
+
exports.InfinityEmbedder = exports.listInfinityModels = exports.probeInfinityHealth = exports.serverRoot = void 0;
|
|
43
37
|
const LoggerService_1 = require("./LoggerService");
|
|
44
38
|
const config_store_1 = require("../config-store");
|
|
45
39
|
const OpenAICompatibleEmbedder_1 = require("./OpenAICompatibleEmbedder");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*/
|
|
52
|
-
function serverRoot(baseURL) {
|
|
53
|
-
return baseURL.replace(/\/+$/, '').replace(/\/(embeddings|v1)$/, '');
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Probe an Infinity-style `/health` endpoint. Resolves `true` when the server
|
|
57
|
-
* answers with a 2xx, `false` on any error or non-2xx. Reused by setup's
|
|
58
|
-
* `waitForInfinity`.
|
|
59
|
-
*/
|
|
60
|
-
async function probeInfinityHealth(baseURL, fetchImpl = fetch, timeoutMs = 2000) {
|
|
61
|
-
try {
|
|
62
|
-
const res = await fetchImpl(`${serverRoot(baseURL)}/health`, {
|
|
63
|
-
method: 'GET',
|
|
64
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
65
|
-
});
|
|
66
|
-
return res.ok;
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* List the model ids an Infinity server currently serves via its OpenAI-style
|
|
74
|
-
* `/models` listing (`{ data: [{ id }] }`). Returns `[]` on any error.
|
|
75
|
-
*/
|
|
76
|
-
async function listInfinityModels(baseURL, fetchImpl = fetch, timeoutMs = 5000) {
|
|
77
|
-
try {
|
|
78
|
-
const res = await fetchImpl(`${serverRoot(baseURL)}/models`, {
|
|
79
|
-
method: 'GET',
|
|
80
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
81
|
-
});
|
|
82
|
-
if (!res.ok)
|
|
83
|
-
return [];
|
|
84
|
-
const data = await res.json();
|
|
85
|
-
return (data.data ?? []).map(m => m.id).filter((id) => Boolean(id));
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
40
|
+
const infinity_provisioning_1 = require("./infinity-provisioning");
|
|
41
|
+
Object.defineProperty(exports, "serverRoot", { enumerable: true, get: function () { return infinity_provisioning_1.serverRoot; } });
|
|
42
|
+
Object.defineProperty(exports, "probeInfinityHealth", { enumerable: true, get: function () { return infinity_provisioning_1.probeInfinityHealth; } });
|
|
43
|
+
Object.defineProperty(exports, "listInfinityModels", { enumerable: true, get: function () { return infinity_provisioning_1.listInfinityModels; } });
|
|
44
|
+
const path = __importStar(require("path"));
|
|
91
45
|
/**
|
|
92
46
|
* Embedder for a local Infinity server. Inherits the OpenAI-style `/embeddings`
|
|
93
47
|
* HTTP path, batching, retry, and reranker wiring from `OpenAICompatibleEmbedder`
|
|
94
|
-
* unchanged — it adds ONLY container-readiness behavior (`ensureModelAvailable
|
|
48
|
+
* unchanged — it adds ONLY container-readiness behavior (`ensureModelAvailable`,
|
|
49
|
+
* `listModels`), delegating to the standalone `infinity-provisioning` primitive
|
|
95
50
|
* because Infinity binds its model set at container startup and exposes no
|
|
96
51
|
* runtime model-pull API.
|
|
97
52
|
*/
|
|
98
53
|
class InfinityEmbedder extends OpenAICompatibleEmbedder_1.OpenAICompatibleEmbedder {
|
|
99
54
|
base;
|
|
100
|
-
doFetchImpl;
|
|
101
|
-
runCommand;
|
|
102
55
|
composePath;
|
|
103
|
-
|
|
104
|
-
log;
|
|
56
|
+
provisioningDeps;
|
|
105
57
|
constructor(config, logger = LoggerService_1.LoggerService.silent(), deps = {}) {
|
|
106
58
|
super(config, logger, deps);
|
|
107
|
-
this.base = serverRoot(config.embedding.baseURL);
|
|
108
|
-
this.doFetchImpl = deps.fetchImpl ?? fetch;
|
|
109
|
-
this.runCommand = deps.runCommand ?? ((cmd, args, env) => {
|
|
110
|
-
const r = (0, child_process_1.spawnSync)(cmd, args, { stdio: 'inherit', env: env ? { ...process.env, ...env } : process.env });
|
|
111
|
-
return { status: r.status, stdout: r.stdout?.toString() };
|
|
112
|
-
});
|
|
59
|
+
this.base = (0, infinity_provisioning_1.serverRoot)(config.embedding.baseURL);
|
|
113
60
|
this.composePath = deps.composePath ?? path.join(config_store_1.CONFIG_DIR, 'docker-compose.yml');
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
servedModels() {
|
|
121
|
-
return listInfinityModels(this.base, this.doFetchImpl);
|
|
122
|
-
}
|
|
123
|
-
/** True when a managed compose file exists AND declares an `infinity` service. */
|
|
124
|
-
hasManagedService() {
|
|
125
|
-
try {
|
|
126
|
-
const content = fs.readFileSync(this.composePath, 'utf-8');
|
|
127
|
-
return /^\s*infinity:\s*$/m.test(content);
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
61
|
+
this.provisioningDeps = {
|
|
62
|
+
fetchImpl: deps.fetchImpl,
|
|
63
|
+
runCommand: deps.runCommand,
|
|
64
|
+
pollIntervalMs: deps.pollIntervalMs,
|
|
65
|
+
readinessTimeoutMs: deps.readinessTimeoutMs,
|
|
66
|
+
};
|
|
132
67
|
}
|
|
133
68
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* Called by `IndexCommand` before `prepareEmbeddingSchema()`.
|
|
69
|
+
* Index-time embed readiness (see `local-backend-provisioning`): verifies only
|
|
70
|
+
* the embedding model — indexing does not use the reranker, so a missing
|
|
71
|
+
* reranker model never blocks or restarts indexing. Whenever a recreate is
|
|
72
|
+
* needed, the full configured set is written so an already-served reranker
|
|
73
|
+
* model is never dropped. Called by `IndexCommand` before indexing.
|
|
141
74
|
*/
|
|
142
75
|
async ensureModelAvailable() {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return;
|
|
151
|
-
throw new Error(`Infinity at ${this.base} serves [${served.join(', ')}] but embedding.model is "${model}". ` +
|
|
152
|
-
`Restart the container with the correct --model-id (e.g. recreate the managed container ` +
|
|
153
|
-
`via rag-setup, or fix embedding.model).`);
|
|
154
|
-
}
|
|
155
|
-
// Unreachable.
|
|
156
|
-
if (this.hasManagedService()) {
|
|
157
|
-
console.log(`Infinity not reachable at ${this.base} — starting the managed container...`);
|
|
158
|
-
this.log.info('InfinityEmbedder: starting managed container', { composePath: this.composePath });
|
|
159
|
-
// Bind the SAME model set the config expects (embed + reranker when enabled),
|
|
160
|
-
// so the auto-started container matches the config — otherwise compose would
|
|
161
|
-
// (re)create it with the compose default and silently drop the reranker /
|
|
162
|
-
// serve the wrong embed model. `--force-recreate` picks up a changed set.
|
|
163
|
-
const modelIds = [
|
|
164
|
-
this.config.embedding.model,
|
|
165
|
-
...(this.config.reranker.enabled && this.config.reranker.model ? [this.config.reranker.model] : []),
|
|
166
|
-
].join(';');
|
|
167
|
-
const r = this.runCommand('docker', ['compose', '-f', this.composePath, 'up', '-d', '--force-recreate', 'infinity'], { INFINITY_MODEL_ID: modelIds });
|
|
168
|
-
if (r.status !== 0) {
|
|
169
|
-
throw new Error(`Failed to start the managed Infinity container (docker compose exited ${r.status}). ` +
|
|
170
|
-
`Ensure Docker is running, or start Infinity manually.`);
|
|
171
|
-
}
|
|
172
|
-
await this.waitForHealthy();
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
throw new Error(`Infinity is not reachable at ${this.base} and no managed container is configured. ` +
|
|
176
|
-
`Start it manually, e.g.:\n` +
|
|
177
|
-
` docker run -it --gpus all -p 7997:7997 michaelf34/infinity:latest \\\n` +
|
|
178
|
-
` v2 --model-id ${model} --port 7997\n` +
|
|
179
|
-
`(drop --gpus all to run on CPU), or re-run rag-setup to provision it.`);
|
|
76
|
+
await (0, infinity_provisioning_1.ensureBackendReady)({
|
|
77
|
+
baseURL: this.config.embedding.baseURL,
|
|
78
|
+
requiredModels: [this.config.embedding.model],
|
|
79
|
+
modelSetToWrite: (0, infinity_provisioning_1.requiredModelSet)(this.config),
|
|
80
|
+
composePath: this.composePath,
|
|
81
|
+
deps: this.provisioningDeps,
|
|
82
|
+
});
|
|
180
83
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
* because a CPU first boot downloads the model set and runs a lengthy per-model
|
|
184
|
-
* warmup before `/health` flips to 200.
|
|
185
|
-
*/
|
|
186
|
-
async waitForHealthy(maxWaitSec = 1800) {
|
|
187
|
-
console.log('Waiting for Infinity to be ready (first boot downloads + warms up the model set, this can take several minutes on CPU)...');
|
|
188
|
-
const deadline = Date.now() + maxWaitSec * 1000;
|
|
189
|
-
while (Date.now() < deadline) {
|
|
190
|
-
if (await this.isHealthy()) {
|
|
191
|
-
console.log(' ✓ Infinity ready.');
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
await new Promise(r => setTimeout(r, this.pollIntervalMs));
|
|
195
|
-
}
|
|
196
|
-
throw new Error(`Infinity did not become ready within ${maxWaitSec}s at ${this.base}.`);
|
|
84
|
+
async listModels() {
|
|
85
|
+
return (0, infinity_provisioning_1.listInfinityModels)(this.base, this.provisioningDeps.fetchImpl ?? fetch, 2000);
|
|
197
86
|
}
|
|
198
87
|
}
|
|
199
88
|
exports.InfinityEmbedder = InfinityEmbedder;
|
|
@@ -3,7 +3,8 @@ import type { ILogger } from '../interfaces/ILogger';
|
|
|
3
3
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
4
|
export declare class LoggerService implements ILogger {
|
|
5
5
|
private readonly logger;
|
|
6
|
-
|
|
6
|
+
private readonly metricsLogger;
|
|
7
|
+
constructor(logger: pino.Logger, metricsLogger?: pino.Logger | null);
|
|
7
8
|
static silent(): LoggerService;
|
|
8
9
|
static fromConfig(cfg: {
|
|
9
10
|
level: string;
|
|
@@ -14,4 +15,5 @@ export declare class LoggerService implements ILogger {
|
|
|
14
15
|
info(msg: string, data?: Record<string, unknown>): void;
|
|
15
16
|
warn(msg: string, data?: Record<string, unknown>): void;
|
|
16
17
|
error(msg: string, data?: Record<string, unknown>): void;
|
|
18
|
+
metric(msg: string, data?: Record<string, unknown>): void;
|
|
17
19
|
}
|
|
@@ -44,8 +44,13 @@ const fs = __importStar(require("fs"));
|
|
|
44
44
|
const DEFAULT_LOG_DIR = path.join(os.homedir(), '.config', 'codesearch-mcp', 'logs');
|
|
45
45
|
class LoggerService {
|
|
46
46
|
logger;
|
|
47
|
-
|
|
47
|
+
// Dedicated `info`-level channel for search telemetry, independent of the
|
|
48
|
+
// main logger's configured level. Null when file logging is disabled so
|
|
49
|
+
// metrics never reach stdout (owned by the MCP JSON transport).
|
|
50
|
+
metricsLogger;
|
|
51
|
+
constructor(logger, metricsLogger = null) {
|
|
48
52
|
this.logger = logger;
|
|
53
|
+
this.metricsLogger = metricsLogger;
|
|
49
54
|
}
|
|
50
55
|
static silent() {
|
|
51
56
|
return new LoggerService((0, pino_1.default)({ level: 'silent' }));
|
|
@@ -66,6 +71,10 @@ class LoggerService {
|
|
|
66
71
|
static buildFromPath(level, filePath) {
|
|
67
72
|
if (filePath)
|
|
68
73
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
74
|
+
// The transport target must accept `info` so the metrics channel can emit
|
|
75
|
+
// even when the main level is `warn`/`error`; the main logger still gates
|
|
76
|
+
// normal calls at `level`, so nothing else leaks below it.
|
|
77
|
+
const targetLevel = pino_1.default.levels.values[level] < pino_1.default.levels.values.info ? level : 'info';
|
|
69
78
|
const transport = filePath
|
|
70
79
|
? pino_1.default.transport({
|
|
71
80
|
targets: [{
|
|
@@ -79,18 +88,23 @@ class LoggerService {
|
|
|
79
88
|
limit: { count: 30 },
|
|
80
89
|
size: '50m',
|
|
81
90
|
},
|
|
82
|
-
level,
|
|
91
|
+
level: targetLevel,
|
|
83
92
|
}],
|
|
84
93
|
})
|
|
85
94
|
: undefined;
|
|
86
95
|
const logger = transport
|
|
87
96
|
? (0, pino_1.default)({ level }, transport)
|
|
88
97
|
: (0, pino_1.default)({ level: 'silent' });
|
|
89
|
-
|
|
98
|
+
// Child logger pinned at `info` regardless of the parent level; only used by
|
|
99
|
+
// metric(). Null when there is no file transport (default stdout must stay
|
|
100
|
+
// clean for the MCP transport).
|
|
101
|
+
const metricsLogger = transport ? logger.child({}, { level: 'info' }) : null;
|
|
102
|
+
return new LoggerService(logger, metricsLogger);
|
|
90
103
|
}
|
|
91
104
|
debug(msg, data) { this.logger.debug(data ?? {}, msg); }
|
|
92
105
|
info(msg, data) { this.logger.info(data ?? {}, msg); }
|
|
93
106
|
warn(msg, data) { this.logger.warn(data ?? {}, msg); }
|
|
94
107
|
error(msg, data) { this.logger.error(data ?? {}, msg); }
|
|
108
|
+
metric(msg, data) { this.metricsLogger?.info(data ?? {}, msg); }
|
|
95
109
|
}
|
|
96
110
|
exports.LoggerService = LoggerService;
|
|
@@ -73,8 +73,8 @@ Pick one workflow based on what you need:
|
|
|
73
73
|
- **read_file_range** — read a specific line range from any indexed file. Expects a **relative path** as returned by search_codebase.
|
|
74
74
|
- **get_dependencies** — show what files a given file imports (direct or transitive). Use to understand what a file depends on.
|
|
75
75
|
- **get_dependents** — show which files import a given file (reverse graph). Use before refactoring to find all callers.
|
|
76
|
-
- **find_symbol** — find where a symbol
|
|
77
|
-
- **get_callers** — find all call sites of a function or method by exact name. Uses the call graph index — faster and more precise than grep. Use after find_symbol to trace where a symbol is actually called.
|
|
76
|
+
- **find_symbol** — find where a symbol is defined by exact name: top-level declarations, class/object-literal members (methods, get/set accessors, constructors, function properties), interface and type-literal method signatures, and namespace-level declarations. Accepts a bare name (\`assertUsersInOrg\`), a qualified name (\`RepairsService.assertUsersInOrg\`, \`NS.Parser.parse\`) to disambiguate same-named members, a quoted name for string-literal members (\`Router.'kebab-name'\`), or a computed name (\`Stack.[Symbol.iterator]\`). Faster than semantic search when you know the name.
|
|
77
|
+
- **get_callers** — find all call sites of a function or method by exact name. Accepts a bare or qualified (\`Class.method\`) symbol, using the same suffix matching as \`find_symbol\`. Uses the call graph index — faster and more precise than grep. A call through an interface-typed receiver is recorded under the interface member (e.g. \`IEmbedder.embedBatch\`), not the implementing class. Use after find_symbol to trace where a symbol is actually called.
|
|
78
78
|
- **list_files** — browse the directory tree of a project path. Use to understand project structure before reading files.
|
|
79
79
|
- **read_file** — read a complete file by **absolute path** with line numbers. Truncates at max_lines with a hint to continue.
|
|
80
80
|
|
|
@@ -86,7 +86,7 @@ Pick one workflow based on what you need:
|
|
|
86
86
|
3. If a result looks **truncated or incomplete**, call \`read_file_range\` with the returned \`file_path\` and \`start_line\`.
|
|
87
87
|
4. For exploratory searches where results span chunk boundaries, pass \`include_neighbors: true\`.
|
|
88
88
|
5. To immediately see a file's imports and callers without extra tool calls, pass \`include_graph_context: true\` to \`search_codebase\`. Use this when doing a quick structural survey; switch to \`get_dependencies\`/\`get_dependents\` for full transitive graphs.
|
|
89
|
-
6. Use \`find_symbol\` when you know the exact name — faster than semantic search. Use \`
|
|
89
|
+
6. Use \`find_symbol\` when you know the exact name — faster than semantic search. Use a qualified name (\`Class.method\`) to disambiguate same-named methods of different classes; a bare name searches every container. Hits marked \`(grep — not in index; may be stale or imprecise)\` come from a fallback text search, not the index — treat them as less reliable. After locating a symbol, use \`get_callers\` to find all call sites — faster and more precise than grep.
|
|
90
90
|
7. Use \`list_files\` to explore project structure before diving into files. Use \`read_file\` to read a complete file by absolute path.
|
|
91
91
|
|
|
92
92
|
### Flow audit (mandatory steps — do not skip 3 and 4)
|
|
@@ -55,7 +55,7 @@ export interface GrepMatch {
|
|
|
55
55
|
filePath: string;
|
|
56
56
|
line: number;
|
|
57
57
|
}
|
|
58
|
-
export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'method' | 'all';
|
|
58
|
+
export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'method' | 'get_accessor' | 'set_accessor' | 'constructor' | 'function_property' | 'method_signature' | 'all';
|
|
59
59
|
export interface CallerRow {
|
|
60
60
|
fromFile: string;
|
|
61
61
|
fromSymbol: string | null;
|
|
@@ -69,6 +69,10 @@ export interface SymbolHit {
|
|
|
69
69
|
column: number;
|
|
70
70
|
kind: Exclude<SymbolKind, 'all'>;
|
|
71
71
|
signature: string;
|
|
72
|
+
/** `container.name`, or `name` when the container is null. */
|
|
73
|
+
qualified: string;
|
|
74
|
+
isStatic: boolean;
|
|
75
|
+
source: 'index' | 'grep';
|
|
72
76
|
}
|
|
73
77
|
export declare class SearcherService {
|
|
74
78
|
private db;
|
|
@@ -106,16 +110,38 @@ export declare class SearcherService {
|
|
|
106
110
|
/**
|
|
107
111
|
* Refuse when the configured embedding model disagrees with the one that built
|
|
108
112
|
* the index — embedding a query into a foreign vector space returns noise. A
|
|
109
|
-
* missing `index_meta`
|
|
113
|
+
* missing `index_meta` row with an empty (or altogether absent) `code_chunk`
|
|
114
|
+
* does not block — but a *populated* `code_chunk` with no `index_meta` row is a
|
|
115
|
+
* pre-`index_meta` install, treated as the legacy `ollama`/`bge-m3` index (same
|
|
116
|
+
* interpretation as the `rag-index` legacy-Ollama guard), read-only — this check
|
|
117
|
+
* never writes `index_meta`.
|
|
110
118
|
*/
|
|
111
119
|
assertIndexMatches(): Promise<void>;
|
|
120
|
+
private compareIndexed;
|
|
112
121
|
listProjects(): Promise<ProjectRow[]>;
|
|
113
122
|
searchChunks(query: string, limit: number, project: string | null, threshold?: number): Promise<SearchResult[]>;
|
|
123
|
+
/**
|
|
124
|
+
* Emit one structured search-tuning line (via `logger.metric`, at `info`
|
|
125
|
+
* regardless of `logging.level`). `candidates` are in cosine order, so a
|
|
126
|
+
* candidate's array index is its cosine rank — `maxCosineRankSurvived` is the
|
|
127
|
+
* deepest cosine position that survived reranking into the returned set. Read
|
|
128
|
+
* it alongside `poolSaturated` to tune `candidateFloor`/`overfetch`/threshold
|
|
129
|
+
* per hardware. Never logs chunk content, embeddings, or credentials.
|
|
130
|
+
*/
|
|
131
|
+
private emitSearchMetrics;
|
|
114
132
|
fetchNeighbors(results: SearchResult[]): Promise<NeighborResult[]>;
|
|
115
133
|
fetchGraphContext(results: SearchResult[]): Promise<Map<string, GraphContext>>;
|
|
116
134
|
getDependencies(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
|
|
117
135
|
getDependents(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
|
|
118
136
|
getCallers(symbol: string, project: string, calleeFile?: string | null): Promise<CallerRow[]>;
|
|
137
|
+
/**
|
|
138
|
+
* One-line warning when any project in `scope` (the given `project`, or
|
|
139
|
+
* every project with `code_chunk`/`code_symbol` rows) is outdated — shown
|
|
140
|
+
* first in `find_symbol`/`get_callers` output. Any failure to determine the
|
|
141
|
+
* scope or read markers only drops the notice (warn logged); the lookup
|
|
142
|
+
* itself is unaffected.
|
|
143
|
+
*/
|
|
144
|
+
outdatedNotice(project: string | null): Promise<string | null>;
|
|
119
145
|
grepSymbol(symbol: string, project: string, excludeFile: string): Promise<{
|
|
120
146
|
matches: GrepMatch[];
|
|
121
147
|
truncated: boolean;
|
|
@@ -123,6 +149,7 @@ export declare class SearcherService {
|
|
|
123
149
|
findSymbol(name: string, project: string | null, kind: SymbolKind): Promise<{
|
|
124
150
|
hits: SymbolHit[];
|
|
125
151
|
truncated: boolean;
|
|
152
|
+
grepFailed: boolean;
|
|
126
153
|
}>;
|
|
127
154
|
private findSymbolFromIndex;
|
|
128
155
|
private grepFindSymbol;
|
|
@@ -133,11 +160,19 @@ export declare class SearcherService {
|
|
|
133
160
|
readFileAbsolute(absPath: string, maxLines: number): Promise<FileRangeResult>;
|
|
134
161
|
readFileRange(filePath: string, startLine: number, endLine: number): Promise<FileRangeResult>;
|
|
135
162
|
private walkDir;
|
|
163
|
+
/**
|
|
164
|
+
* ERE pattern for a grep-fallback kind, or null when that kind is never
|
|
165
|
+
* grepped (`function_property`/`method_signature` are index-only; a bare
|
|
166
|
+
* `constructor` search only makes sense when the name itself is `constructor`).
|
|
167
|
+
*/
|
|
136
168
|
private buildFindSymbolPattern;
|
|
169
|
+
/** 1-based index of the first whole-word occurrence of `name` in `line`, or -1. */
|
|
170
|
+
private static wholeWordIndex;
|
|
137
171
|
private inferKind;
|
|
138
172
|
/**
|
|
139
|
-
* Async wrapper around grep to avoid blocking the event loop.
|
|
140
|
-
*
|
|
173
|
+
* Async wrapper around grep to avoid blocking the event loop. `ok: false`
|
|
174
|
+
* means grep could not be spawned or exited with status ≥ 2 (distinguishable
|
|
175
|
+
* from a clean "no matches", which is `ok: true, output: null`).
|
|
141
176
|
*/
|
|
142
177
|
private spawnGrep;
|
|
143
178
|
}
|