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.
Files changed (64) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +146 -41
  3. package/dist/app.js +4 -19
  4. package/dist/src/commands/IndexCommand.js +68 -7
  5. package/dist/src/config-error.d.ts +7 -0
  6. package/dist/src/config-error.js +14 -0
  7. package/dist/src/config-store.d.ts +30 -12
  8. package/dist/src/config-store.js +81 -26
  9. package/dist/src/config.d.ts +5 -4
  10. package/dist/src/config.js +14 -16
  11. package/dist/src/constants.d.ts +8 -2
  12. package/dist/src/constants.js +8 -5
  13. package/dist/src/db/index.d.ts +1 -1
  14. package/dist/src/db/index.js +2 -1
  15. package/dist/src/db/schema.d.ts +75 -0
  16. package/dist/src/db/schema.js +10 -1
  17. package/dist/src/enriched-prompt.d.ts +8 -0
  18. package/dist/src/enriched-prompt.js +87 -0
  19. package/dist/src/help.js +1 -1
  20. package/dist/src/interfaces/IAstParser.d.ts +18 -2
  21. package/dist/src/interfaces/IDatabase.d.ts +31 -0
  22. package/dist/src/interfaces/ILogger.d.ts +7 -0
  23. package/dist/src/interfaces/ISearcher.d.ts +3 -0
  24. package/dist/src/services/AstChunker.d.ts +11 -0
  25. package/dist/src/services/AstChunker.js +44 -0
  26. package/dist/src/services/AstParserService.d.ts +39 -7
  27. package/dist/src/services/AstParserService.js +504 -104
  28. package/dist/src/services/DatabaseService.d.ts +17 -0
  29. package/dist/src/services/DatabaseService.js +130 -2
  30. package/dist/src/services/EmbedderFactory.d.ts +1 -1
  31. package/dist/src/services/EmbedderFactory.js +2 -5
  32. package/dist/src/services/IndexerService.d.ts +4 -14
  33. package/dist/src/services/IndexerService.js +54 -156
  34. package/dist/src/services/InfinityEmbedder.d.ts +14 -55
  35. package/dist/src/services/InfinityEmbedder.js +30 -141
  36. package/dist/src/services/LoggerService.d.ts +3 -1
  37. package/dist/src/services/LoggerService.js +17 -3
  38. package/dist/src/services/McpServer.js +3 -3
  39. package/dist/src/services/SearcherService.d.ts +39 -4
  40. package/dist/src/services/SearcherService.js +254 -81
  41. package/dist/src/services/WebServerService.d.ts +41 -3
  42. package/dist/src/services/WebServerService.js +90 -2
  43. package/dist/src/services/infinity-provisioning.d.ts +115 -0
  44. package/dist/src/services/infinity-provisioning.js +516 -0
  45. package/dist/src/services/legacy-ollama-index.d.ts +24 -0
  46. package/dist/src/services/legacy-ollama-index.js +61 -0
  47. package/dist/src/services/reranker.d.ts +3 -3
  48. package/dist/src/services/reranker.js +3 -3
  49. package/dist/src/setup.d.ts +37 -0
  50. package/dist/src/setup.js +111 -157
  51. package/dist/src/symbol-name.d.ts +44 -0
  52. package/dist/src/symbol-name.js +68 -0
  53. package/dist/src/symbol-rows.d.ts +12 -0
  54. package/dist/src/symbol-rows.js +38 -0
  55. package/dist/src/tools/FindSymbolTool.js +30 -7
  56. package/dist/src/tools/GetCallersTool.js +20 -5
  57. package/docker/docker-compose.yml +5 -15
  58. package/docs/architecture.svg +103 -0
  59. package/docs/web-dashboard.md +36 -17
  60. package/package.json +1 -6
  61. package/dist/src/commands/BackfillCommand.d.ts +0 -7
  62. package/dist/src/commands/BackfillCommand.js +0 -37
  63. package/dist/src/services/OllamaEmbedder.d.ts +0 -25
  64. package/dist/src/services/OllamaEmbedder.js +0 -111
@@ -13,7 +13,6 @@ declare const jsonSchema: z.ZodObject<{
13
13
  }, z.core.$strip>>;
14
14
  embedding: z.ZodOptional<z.ZodObject<{
15
15
  provider: z.ZodOptional<z.ZodEnum<{
16
- ollama: "ollama";
17
16
  "openai-compatible": "openai-compatible";
18
17
  voyage: "voyage";
19
18
  infinity: "infinity";
@@ -49,6 +48,7 @@ declare const jsonSchema: z.ZodObject<{
49
48
  model: z.ZodOptional<z.ZodString>;
50
49
  url: z.ZodOptional<z.ZodString>;
51
50
  overfetch: z.ZodOptional<z.ZodNumber>;
51
+ candidateFloor: z.ZodOptional<z.ZodNumber>;
52
52
  }, z.core.$strip>>;
53
53
  logging: z.ZodOptional<z.ZodObject<{
54
54
  level: z.ZodOptional<z.ZodEnum<{
@@ -58,6 +58,7 @@ declare const jsonSchema: z.ZodObject<{
58
58
  warn: "warn";
59
59
  }>>;
60
60
  dir: z.ZodOptional<z.ZodString>;
61
+ searchMetrics: z.ZodOptional<z.ZodBoolean>;
61
62
  }, z.core.$strip>>;
62
63
  workspaceRoot: z.ZodOptional<z.ZodString>;
63
64
  }, z.core.$strip>;
@@ -77,9 +78,9 @@ export declare const ALL_KEYS: string[];
77
78
  */
78
79
  export declare const CONFIG_DEFAULTS: {
79
80
  readonly 'db.port': 5433;
80
- readonly 'embedding.provider': "ollama";
81
+ readonly 'embedding.provider': "infinity";
81
82
  readonly 'embedding.apiKey': "";
82
- readonly 'embedding.model': "bge-m3";
83
+ readonly 'embedding.model': "jinaai/jina-embeddings-v2-base-code";
83
84
  readonly 'embedding.batchSize': 32;
84
85
  readonly 'indexing.chunkSize': 1500;
85
86
  readonly 'indexing.chunkOverlap': 200;
@@ -89,20 +90,30 @@ export declare const CONFIG_DEFAULTS: {
89
90
  readonly 'search.similarityThreshold': 0.6;
90
91
  readonly 'search.defaultLimit': 5;
91
92
  readonly 'search.maxLimit': 20;
92
- readonly 'reranker.enabled': false;
93
+ readonly 'reranker.enabled': true;
93
94
  readonly 'reranker.provider': "openai-compatible";
94
95
  readonly 'reranker.apiKey': "";
95
- readonly 'reranker.model': "";
96
- readonly 'reranker.overfetch': 3;
96
+ readonly 'reranker.model': "mixedbread-ai/mxbai-rerank-xsmall-v1";
97
+ readonly 'reranker.overfetch': 5;
98
+ readonly 'reranker.candidateFloor': 80;
97
99
  readonly 'logging.level': "error";
100
+ readonly 'logging.searchMetrics': false;
98
101
  };
99
102
  export declare function readConfigJson(): JsonConfig | null;
103
+ /**
104
+ * Detect a raw (pre-migration) config object that still selects the removed
105
+ * `ollama` embedding provider — explicitly, via a legacy top-level `ollama`
106
+ * block, or implicitly (no `provider` set, but `baseURL`/`model` point at
107
+ * Ollama's defaults) — and throw a `ConfigError` with migration guidance
108
+ * instead of letting it validate under a silently reinterpreted provider.
109
+ * Runs before `migrateRawConfig` so a legacy `ollama.*` block is never
110
+ * converted into an `embedding` block first. Pure — never mutates `raw`.
111
+ */
112
+ export declare function detectFormerOllamaConfig(raw: unknown): void;
100
113
  /**
101
114
  * Migrate a legacy raw config object to the current shape, in place of a
102
115
  * versioned migration system. Pure — returns a new object, never mutates input.
103
116
  *
104
- * - Legacy `ollama.host`/`ollama.model` → `embedding.{baseURL,model}` with
105
- * `provider: 'ollama'` (only when `embedding` is not already present).
106
117
  * - Removed translation keys (`search.autoTranslate`, `search.translateModel`)
107
118
  * are dropped so they neither validate-fail nor take effect.
108
119
  * - Legacy `reranker.provider: 'ollama'` (a dead value — Ollama has no rerank
@@ -110,13 +121,20 @@ export declare function readConfigJson(): JsonConfig | null;
110
121
  * dropped so it re-derives to `/rerank`. Otherwise the tightened enum would
111
122
  * fail validation on existing configs and lock the user out.
112
123
  *
113
- * `ollama` itself is left for the schema to strip. Exported for unit testing.
124
+ * A legacy top-level `ollama` block reaches here only when `detectFormerOllamaConfig`
125
+ * did not already reject it (i.e. an `embedding` block is also present); it is
126
+ * dropped rather than merged. Exported for unit testing.
114
127
  */
115
128
  export declare function migrateRawConfig(raw: unknown): unknown;
116
129
  /**
117
- * Validate a parsed config object against the Zod schema. Unknown keys (e.g.
118
- * the "$schema" editor reference) are stripped. Throws with a caller-friendly
119
- * message on validation failure. Pure — no file I/O.
130
+ * Validate a raw (pre-migration) config object against the Zod schema: first
131
+ * `detectFormerOllamaConfig` (a `ConfigError` on a former-Ollama config takes
132
+ * priority over both migration and the regular Zod enum rejection), then
133
+ * `migrateRawConfig`, then the schema itself. Unknown keys (e.g. the "$schema"
134
+ * editor reference) are stripped. Throws with a caller-friendly message on
135
+ * validation failure. The single entry point for both `readConfigJson` and
136
+ * `PUT /api/config`, so both apply identical rules with an identical message.
137
+ * Pure — no file I/O.
120
138
  */
121
139
  export declare function parseConfig(raw: unknown, source?: string): JsonConfig;
122
140
  /** Build the JSON Schema object generated from the Zod config schema. Pure. */
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CONFIG_DEFAULTS = exports.ALL_KEYS = exports.ENV_FILE = exports.SCHEMA_FILE = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
37
37
  exports.readConfigJson = readConfigJson;
38
+ exports.detectFormerOllamaConfig = detectFormerOllamaConfig;
38
39
  exports.migrateRawConfig = migrateRawConfig;
39
40
  exports.parseConfig = parseConfig;
40
41
  exports.buildJsonSchema = buildJsonSchema;
@@ -48,6 +49,7 @@ const path = __importStar(require("path"));
48
49
  const os = __importStar(require("os"));
49
50
  const zod_1 = require("zod");
50
51
  const constants_1 = require("./constants");
52
+ const config_error_1 = require("./config-error");
51
53
  exports.CONFIG_DIR = path.join(os.homedir(), '.config', 'codesearch-mcp');
52
54
  exports.CONFIG_FILE = path.join(exports.CONFIG_DIR, 'config.json');
53
55
  exports.SCHEMA_FILE = path.join(exports.CONFIG_DIR, 'config.schema.json');
@@ -66,10 +68,10 @@ const jsonSchema = zod_1.z.object({
66
68
  name: zod_1.z.string().describe('Database name; defaults to rag_<os-username>, auto-created on first run').optional(),
67
69
  }).optional(),
68
70
  embedding: zod_1.z.object({
69
- provider: zod_1.z.enum(['ollama', 'openai-compatible', 'voyage', 'infinity']).describe('Embedding backend (default ollama)').optional(),
70
- baseURL: zod_1.z.string().describe('Backend base URL. ollama: http://localhost:11434 · OpenAI: https://api.openai.com/v1 · Infinity/TEI/vLLM: http://localhost:7997 · Voyage: https://api.voyageai.com/v1').optional(),
71
+ provider: zod_1.z.enum(['openai-compatible', 'voyage', 'infinity']).describe('Embedding backend (default infinity)').optional(),
72
+ baseURL: zod_1.z.string().describe('Backend base URL. Infinity/TEI/vLLM/Ollama-via-openai-compatible: http://localhost:7997 (Infinity default) · OpenAI: https://api.openai.com/v1 · Voyage: https://api.voyageai.com/v1').optional(),
71
73
  apiKey: zod_1.z.string().describe('API key. Use env:VAR_NAME to read from ~/.config/codesearch-mcp/.env or the environment (recommended). Empty for local servers.').optional(),
72
- model: zod_1.z.string().describe('Embedding model (default bge-m3)').optional(),
74
+ model: zod_1.z.string().describe('Embedding model (default jinaai/jina-embeddings-v2-base-code)').optional(),
73
75
  dimensions: zod_1.z.number().int().describe('Vector width override; required for models whose native width exceeds 2000 (Matryoshka reduction). Omit to derive from the model/registry/probe.').optional(),
74
76
  batchSize: zod_1.z.number().int().describe('Texts per batch embedding request (default 32)').optional(),
75
77
  }).optional(),
@@ -87,16 +89,18 @@ const jsonSchema = zod_1.z.object({
87
89
  }).optional(),
88
90
  reranker: zod_1.z.object({
89
91
  enabled: zod_1.z.boolean().describe('Enable cross-encoder reranking; requires reranker.model').optional(),
90
- provider: zod_1.z.enum(['openai-compatible']).describe('Rerank backend. Only openai-compatible /rerank servers (Infinity, TEI, vLLM) — Ollama has no rerank API.').optional(),
92
+ provider: zod_1.z.enum(['openai-compatible']).describe('Rerank backend. Only openai-compatible /rerank servers (Infinity, TEI, vLLM).').optional(),
91
93
  baseURL: zod_1.z.string().describe('Rerank server base URL (defaults to embedding.baseURL — Infinity serves both).').optional(),
92
94
  apiKey: zod_1.z.string().describe('API key, or env:VAR_NAME. Empty for local servers.').optional(),
93
95
  model: zod_1.z.string().describe('Reranker model name (e.g. qllama/bge-reranker-v2-m3)').optional(),
94
96
  url: zod_1.z.string().describe('Full URL of the rerank endpoint (default derived from provider + baseURL)').optional(),
95
- overfetch: zod_1.z.number().int().min(1).max(20).describe('Candidate multiplier before reranking (limit × N, default 3)').optional(),
97
+ overfetch: zod_1.z.number().int().min(1).max(20).describe('Candidate multiplier before reranking (limit × N, default 5)').optional(),
98
+ candidateFloor: zod_1.z.number().int().min(1).describe('Minimum candidate pool fed to the reranker regardless of limit; fetchLimit = min(max(limit × overfetch, candidateFloor), 200) (default 80)').optional(),
96
99
  }).optional(),
97
100
  logging: zod_1.z.object({
98
101
  level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).describe('File log level (default error)').optional(),
99
102
  dir: zod_1.z.string().describe('Base log directory; "false" to disable file logging').optional(),
103
+ searchMetrics: zod_1.z.boolean().describe('Emit one structured search-tuning line (info level) per search, independent of level (default false)').optional(),
100
104
  }).optional(),
101
105
  workspaceRoot: zod_1.z.string().describe('Full path to the directory containing all your projects').optional(),
102
106
  });
@@ -127,8 +131,10 @@ const FIELD_TYPES = {
127
131
  'reranker.model': 'string',
128
132
  'reranker.url': 'string',
129
133
  'reranker.overfetch': 'number',
134
+ 'reranker.candidateFloor': 'number',
130
135
  'logging.level': 'string',
131
136
  'logging.dir': 'string',
137
+ 'logging.searchMetrics': 'boolean',
132
138
  'workspaceRoot': 'string',
133
139
  };
134
140
  exports.ALL_KEYS = Object.keys(FIELD_TYPES);
@@ -146,7 +152,7 @@ exports.ALL_KEYS = Object.keys(FIELD_TYPES);
146
152
  */
147
153
  exports.CONFIG_DEFAULTS = {
148
154
  'db.port': 5433,
149
- 'embedding.provider': 'ollama',
155
+ 'embedding.provider': 'infinity',
150
156
  'embedding.apiKey': '',
151
157
  'embedding.model': constants_1.DEFAULT_EMBED_MODEL,
152
158
  'embedding.batchSize': 32,
@@ -158,12 +164,14 @@ exports.CONFIG_DEFAULTS = {
158
164
  'search.similarityThreshold': 0.6,
159
165
  'search.defaultLimit': 5,
160
166
  'search.maxLimit': 20,
161
- 'reranker.enabled': false,
167
+ 'reranker.enabled': true,
162
168
  'reranker.provider': 'openai-compatible',
163
169
  'reranker.apiKey': '',
164
- 'reranker.model': '',
165
- 'reranker.overfetch': 3,
170
+ 'reranker.model': 'mixedbread-ai/mxbai-rerank-xsmall-v1',
171
+ 'reranker.overfetch': 5,
172
+ 'reranker.candidateFloor': 80,
166
173
  'logging.level': 'error',
174
+ 'logging.searchMetrics': false,
167
175
  };
168
176
  function readConfigJson() {
169
177
  if (!fs.existsSync(exports.CONFIG_FILE))
@@ -183,14 +191,61 @@ function readConfigJson() {
183
191
  throw new Error(`Invalid JSON in ${exports.CONFIG_FILE}: ${e.message}\n` +
184
192
  `Fix the file manually or delete it and run: rag-setup`);
185
193
  }
186
- return parseConfig(migrateRawConfig(parsed), exports.CONFIG_FILE);
194
+ return parseConfig(parsed, exports.CONFIG_FILE);
195
+ }
196
+ const OLLAMA_MIGRATION_REMEDY = 'The "ollama" embedding provider has been removed. Point Ollama through the ' +
197
+ '"openai-compatible" provider instead — set embedding.provider to "openai-compatible" ' +
198
+ 'and embedding.baseURL to "http://localhost:11434/v1" — via `rag-config edit` (or by ' +
199
+ 'editing the config file directly) or by re-running `rag-setup`. After switching, rebuild ' +
200
+ 'the index with `rag-index --reindex`.';
201
+ /** Strip a trailing `:tag` (Docker-style) from a model name, if present. */
202
+ function stripModelTag(model) {
203
+ return model.replace(/:[^:]*$/, '');
204
+ }
205
+ /**
206
+ * Detect a raw (pre-migration) config object that still selects the removed
207
+ * `ollama` embedding provider — explicitly, via a legacy top-level `ollama`
208
+ * block, or implicitly (no `provider` set, but `baseURL`/`model` point at
209
+ * Ollama's defaults) — and throw a `ConfigError` with migration guidance
210
+ * instead of letting it validate under a silently reinterpreted provider.
211
+ * Runs before `migrateRawConfig` so a legacy `ollama.*` block is never
212
+ * converted into an `embedding` block first. Pure — never mutates `raw`.
213
+ */
214
+ function detectFormerOllamaConfig(raw) {
215
+ if (raw == null || typeof raw !== 'object')
216
+ return;
217
+ const obj = raw;
218
+ const embedding = obj.embedding;
219
+ if (embedding != null && typeof embedding === 'object') {
220
+ const e = embedding;
221
+ if (e.provider === 'ollama') {
222
+ throw new config_error_1.ConfigError(`Config selects the "ollama" embedding provider, which is no longer supported. ${OLLAMA_MIGRATION_REMEDY}`);
223
+ }
224
+ if (e.provider === undefined) {
225
+ if (typeof e.baseURL === 'string') {
226
+ let port;
227
+ try {
228
+ port = new URL(e.baseURL).port;
229
+ }
230
+ catch { /* unparsable — not a signal */ }
231
+ if (port === '11434') {
232
+ throw new config_error_1.ConfigError(`Config's embedding.baseURL targets Ollama's default port (11434) with no embedding.provider set. ${OLLAMA_MIGRATION_REMEDY}`);
233
+ }
234
+ }
235
+ if (typeof e.model === 'string' && constants_1.FORMER_OLLAMA_MODELS.includes(stripModelTag(e.model))) {
236
+ throw new config_error_1.ConfigError(`Config's embedding.model ("${e.model}") was formerly registered for Ollama, with no embedding.provider set. ${OLLAMA_MIGRATION_REMEDY}`);
237
+ }
238
+ }
239
+ return;
240
+ }
241
+ if (obj.ollama != null && typeof obj.ollama === 'object') {
242
+ throw new config_error_1.ConfigError(`Config has a legacy "ollama" configuration block. ${OLLAMA_MIGRATION_REMEDY}`);
243
+ }
187
244
  }
188
245
  /**
189
246
  * Migrate a legacy raw config object to the current shape, in place of a
190
247
  * versioned migration system. Pure — returns a new object, never mutates input.
191
248
  *
192
- * - Legacy `ollama.host`/`ollama.model` → `embedding.{baseURL,model}` with
193
- * `provider: 'ollama'` (only when `embedding` is not already present).
194
249
  * - Removed translation keys (`search.autoTranslate`, `search.translateModel`)
195
250
  * are dropped so they neither validate-fail nor take effect.
196
251
  * - Legacy `reranker.provider: 'ollama'` (a dead value — Ollama has no rerank
@@ -198,21 +253,14 @@ function readConfigJson() {
198
253
  * dropped so it re-derives to `/rerank`. Otherwise the tightened enum would
199
254
  * fail validation on existing configs and lock the user out.
200
255
  *
201
- * `ollama` itself is left for the schema to strip. Exported for unit testing.
256
+ * A legacy top-level `ollama` block reaches here only when `detectFormerOllamaConfig`
257
+ * did not already reject it (i.e. an `embedding` block is also present); it is
258
+ * dropped rather than merged. Exported for unit testing.
202
259
  */
203
260
  function migrateRawConfig(raw) {
204
261
  if (raw == null || typeof raw !== 'object')
205
262
  return raw;
206
263
  const obj = JSON.parse(JSON.stringify(raw));
207
- const legacyOllama = obj.ollama;
208
- if (!obj.embedding && legacyOllama && typeof legacyOllama === 'object') {
209
- const embedding = { provider: 'ollama' };
210
- if (typeof legacyOllama.host === 'string')
211
- embedding.baseURL = legacyOllama.host;
212
- if (typeof legacyOllama.model === 'string')
213
- embedding.model = legacyOllama.model;
214
- obj.embedding = embedding;
215
- }
216
264
  delete obj.ollama;
217
265
  if (obj.search && typeof obj.search === 'object') {
218
266
  delete obj.search.autoTranslate;
@@ -227,12 +275,19 @@ function migrateRawConfig(raw) {
227
275
  return obj;
228
276
  }
229
277
  /**
230
- * Validate a parsed config object against the Zod schema. Unknown keys (e.g.
231
- * the "$schema" editor reference) are stripped. Throws with a caller-friendly
232
- * message on validation failure. Pure — no file I/O.
278
+ * Validate a raw (pre-migration) config object against the Zod schema: first
279
+ * `detectFormerOllamaConfig` (a `ConfigError` on a former-Ollama config takes
280
+ * priority over both migration and the regular Zod enum rejection), then
281
+ * `migrateRawConfig`, then the schema itself. Unknown keys (e.g. the "$schema"
282
+ * editor reference) are stripped. Throws with a caller-friendly message on
283
+ * validation failure. The single entry point for both `readConfigJson` and
284
+ * `PUT /api/config`, so both apply identical rules with an identical message.
285
+ * Pure — no file I/O.
233
286
  */
234
287
  function parseConfig(raw, source = exports.CONFIG_FILE) {
235
- const result = jsonSchema.safeParse(raw);
288
+ detectFormerOllamaConfig(raw);
289
+ const migrated = migrateRawConfig(raw);
290
+ const result = jsonSchema.safeParse(migrated);
236
291
  if (!result.success) {
237
292
  const issues = result.error.issues.map(i => ` ${i.path.join('.')}: ${i.message}`).join('\n');
238
293
  throw new Error(`Invalid config in ${source}:\n${issues}\n` +
@@ -1,6 +1,5 @@
1
- export declare class ConfigError extends Error {
2
- constructor(message: string);
3
- }
1
+ import { ConfigError } from './config-error';
2
+ export { ConfigError };
4
3
  /**
5
4
  * Resolve a config value that may reference an environment variable. A value of
6
5
  * the form `env:VAR_NAME` resolves to `process.env.VAR_NAME`; any other string
@@ -26,7 +25,7 @@ export declare class Config {
26
25
  * locking the user out of `rag-web`/`rag-mcp`.
27
26
  */
28
27
  get embedding(): {
29
- provider: "ollama" | "openai-compatible" | "voyage" | "infinity";
28
+ provider: "openai-compatible" | "voyage" | "infinity";
30
29
  baseURL: string;
31
30
  apiKey: string;
32
31
  model: string;
@@ -42,6 +41,7 @@ export declare class Config {
42
41
  model: string;
43
42
  url: string;
44
43
  overfetch: number;
44
+ candidateFloor: number;
45
45
  };
46
46
  get indexer(): {
47
47
  chunkSize: number;
@@ -57,6 +57,7 @@ export declare class Config {
57
57
  };
58
58
  get logging(): {
59
59
  level: "error" | "debug" | "info" | "warn";
60
+ searchMetrics: boolean;
60
61
  dir?: string | undefined;
61
62
  };
62
63
  get workspaceRoot(): string;
@@ -38,13 +38,8 @@ exports.resolveEnvRef = resolveEnvRef;
38
38
  const os = __importStar(require("os"));
39
39
  const zod_1 = require("zod");
40
40
  const config_store_1 = require("./config-store");
41
- class ConfigError extends Error {
42
- constructor(message) {
43
- super(message);
44
- this.name = 'ConfigError';
45
- }
46
- }
47
- exports.ConfigError = ConfigError;
41
+ const config_error_1 = require("./config-error");
42
+ Object.defineProperty(exports, "ConfigError", { enumerable: true, get: function () { return config_error_1.ConfigError; } });
48
43
  /**
49
44
  * Resolve a config value that may reference an environment variable. A value of
50
45
  * the form `env:VAR_NAME` resolves to `process.env.VAR_NAME`; any other string
@@ -58,24 +53,23 @@ function resolveEnvRef(value, env = process.env) {
58
53
  const name = value.slice(4).trim();
59
54
  const resolved = name ? env[name] : undefined;
60
55
  if (resolved == null || resolved === '') {
61
- throw new ConfigError(`Config references environment variable "${name}" (as "env:${name}") but it is not set. ` +
56
+ throw new config_error_1.ConfigError(`Config references environment variable "${name}" (as "env:${name}") but it is not set. ` +
62
57
  `Define ${name} in ${config_store_1.ENV_FILE} or export it in the environment.`);
63
58
  }
64
59
  return resolved;
65
60
  }
66
61
  const DEFAULT_BASE_URLS = {
67
- 'ollama': 'http://localhost:11434',
68
62
  'openai-compatible': 'https://api.openai.com/v1',
69
63
  'voyage': 'https://api.voyageai.com/v1',
70
64
  'infinity': 'http://localhost:7997',
71
65
  };
72
66
  function defaultBaseURL(provider) {
73
- return DEFAULT_BASE_URLS[provider] ?? DEFAULT_BASE_URLS.ollama;
67
+ return DEFAULT_BASE_URLS[provider] ?? DEFAULT_BASE_URLS.infinity;
74
68
  }
75
69
  // Reranking is served only by OpenAI-compatible `/rerank` endpoints (Infinity,
76
- // TEI, vLLM, Jina/Cohere-shape). Ollama has no rerank API, so there is a single
77
- // endpoint shape. Normalize a trailing `/embeddings` or `/v1` to the server root
78
- // so a baseURL written with an OpenAI-style suffix still yields `<root>/rerank`.
70
+ // TEI, vLLM, Jina/Cohere-shape) — a single endpoint shape. Normalize a trailing
71
+ // `/embeddings` or `/v1` to the server root so a baseURL written with an
72
+ // OpenAI-style suffix still yields `<root>/rerank`.
79
73
  function deriveRerankUrl(baseURL) {
80
74
  const root = baseURL.replace(/\/+$/, '').replace(/\/(embeddings|v1)$/, '');
81
75
  return `${root}/rerank`;
@@ -89,7 +83,7 @@ const resolvedSchema = zod_1.z.object({
89
83
  database: zod_1.z.string().min(1),
90
84
  }),
91
85
  embedding: zod_1.z.object({
92
- provider: zod_1.z.enum(['ollama', 'openai-compatible', 'voyage', 'infinity']).default(config_store_1.CONFIG_DEFAULTS['embedding.provider']),
86
+ provider: zod_1.z.enum(['openai-compatible', 'voyage', 'infinity']).default(config_store_1.CONFIG_DEFAULTS['embedding.provider']),
93
87
  baseURL: zod_1.z.string().min(1),
94
88
  apiKey: zod_1.z.string().default(config_store_1.CONFIG_DEFAULTS['embedding.apiKey']),
95
89
  model: zod_1.z.string().min(1).default(config_store_1.CONFIG_DEFAULTS['embedding.model']),
@@ -104,6 +98,7 @@ const resolvedSchema = zod_1.z.object({
104
98
  model: zod_1.z.string().default(config_store_1.CONFIG_DEFAULTS['reranker.model']),
105
99
  url: zod_1.z.string().min(1),
106
100
  overfetch: zod_1.z.number().int().min(1).max(20).default(config_store_1.CONFIG_DEFAULTS['reranker.overfetch']),
101
+ candidateFloor: zod_1.z.number().int().min(1).default(config_store_1.CONFIG_DEFAULTS['reranker.candidateFloor']),
107
102
  }),
108
103
  indexer: zod_1.z.object({
109
104
  chunkSize: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['indexing.chunkSize']),
@@ -120,6 +115,7 @@ const resolvedSchema = zod_1.z.object({
120
115
  logging: zod_1.z.object({
121
116
  level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).default(config_store_1.CONFIG_DEFAULTS['logging.level']),
122
117
  dir: zod_1.z.string().optional(),
118
+ searchMetrics: zod_1.z.boolean().default(config_store_1.CONFIG_DEFAULTS['logging.searchMetrics']),
123
119
  }),
124
120
  workspaceRoot: zod_1.z.string().default(''),
125
121
  });
@@ -143,7 +139,7 @@ class Config {
143
139
  constructor() {
144
140
  const raw = (0, config_store_1.readConfigJson)();
145
141
  if (!raw)
146
- throw new ConfigError('Config not found. Run rag-setup.');
142
+ throw new config_error_1.ConfigError('Config not found. Run rag-setup.');
147
143
  const embProvider = (raw.embedding?.provider ?? config_store_1.CONFIG_DEFAULTS['embedding.provider']);
148
144
  const embBaseURL = raw.embedding?.baseURL ?? defaultBaseURL(embProvider);
149
145
  const rerankBaseURL = raw.reranker?.baseURL ?? embBaseURL;
@@ -172,6 +168,7 @@ class Config {
172
168
  model: raw.reranker?.model,
173
169
  url: rerankUrl,
174
170
  overfetch: raw.reranker?.overfetch,
171
+ candidateFloor: raw.reranker?.candidateFloor,
175
172
  },
176
173
  indexer: {
177
174
  chunkSize: raw.indexing?.chunkSize,
@@ -188,6 +185,7 @@ class Config {
188
185
  logging: {
189
186
  level: raw.logging?.level,
190
187
  dir: raw.logging?.dir,
188
+ searchMetrics: raw.logging?.searchMetrics,
191
189
  },
192
190
  workspaceRoot: raw.workspaceRoot,
193
191
  });
@@ -195,7 +193,7 @@ class Config {
195
193
  const issues = result.error.issues
196
194
  .map(i => ` ${i.path.join('.')}: ${i.message}`)
197
195
  .join('\n');
198
- throw new ConfigError(`Invalid config:\n${issues}\nRun rag-setup or: rag-config set <key> <value>`);
196
+ throw new config_error_1.ConfigError(`Invalid config:\n${issues}\nRun rag-setup or: rag-config set <key> <value>`);
199
197
  }
200
198
  this.data = result.data;
201
199
  }
@@ -1,4 +1,4 @@
1
- export type EmbeddingProvider = 'ollama' | 'openai-compatible' | 'voyage' | 'infinity';
1
+ export type EmbeddingProvider = 'openai-compatible' | 'voyage' | 'infinity';
2
2
  /**
3
3
  * pgvector's HNSW / IVFFlat index hard limit. The effective embedding dimension
4
4
  * must not exceed this; larger cloud models must reduce via the Matryoshka
@@ -23,7 +23,13 @@ export interface ModelInfo {
23
23
  * time (see `resolveDimension`).
24
24
  */
25
25
  export declare const MODEL_REGISTRY: Record<string, ModelInfo>;
26
- export declare const DEFAULT_EMBED_MODEL = "bge-m3";
26
+ export declare const DEFAULT_EMBED_MODEL = "jinaai/jina-embeddings-v2-base-code";
27
+ /**
28
+ * Former Ollama registry model names, kept only so config validation can
29
+ * recognise an implicit (provider-less) config that still targets Ollama —
30
+ * see `detectFormerOllamaConfig` in `config-store.ts`.
31
+ */
32
+ export declare const FORMER_OLLAMA_MODELS: readonly ["bge-m3", "nomic-embed-text", "mxbai-embed-large"];
27
33
  /** Advisory dimension for a model, or `undefined` when the model is unknown. */
28
34
  export declare function registryDimension(model: string): number | undefined;
29
35
  /**
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_EMBED_MODEL = exports.MODEL_REGISTRY = exports.MAX_EMBED_DIM = void 0;
3
+ exports.FORMER_OLLAMA_MODELS = exports.DEFAULT_EMBED_MODEL = exports.MODEL_REGISTRY = exports.MAX_EMBED_DIM = void 0;
4
4
  exports.registryDimension = registryDimension;
5
5
  exports.resolveDimension = resolveDimension;
6
6
  /**
@@ -16,9 +16,6 @@ exports.MAX_EMBED_DIM = 2000;
16
16
  * time (see `resolveDimension`).
17
17
  */
18
18
  exports.MODEL_REGISTRY = {
19
- 'bge-m3': { provider: 'ollama', dimensions: 1024 },
20
- 'nomic-embed-text': { provider: 'ollama', dimensions: 768 },
21
- 'mxbai-embed-large': { provider: 'ollama', dimensions: 1024 },
22
19
  'text-embedding-3-small': { provider: 'openai-compatible', dimensions: 1536, dimConfigurable: true },
23
20
  'text-embedding-3-large': { provider: 'openai-compatible', dimensions: 3072, dimConfigurable: true, maxIndexable: 2000 },
24
21
  'text-embedding-ada-002': { provider: 'openai-compatible', dimensions: 1536 },
@@ -28,7 +25,13 @@ exports.MODEL_REGISTRY = {
28
25
  'BAAI/bge-m3': { provider: 'infinity', dimensions: 1024 },
29
26
  'jinaai/jina-embeddings-v2-base-code': { provider: 'infinity', dimensions: 768 },
30
27
  };
31
- exports.DEFAULT_EMBED_MODEL = 'bge-m3';
28
+ exports.DEFAULT_EMBED_MODEL = 'jinaai/jina-embeddings-v2-base-code';
29
+ /**
30
+ * Former Ollama registry model names, kept only so config validation can
31
+ * recognise an implicit (provider-less) config that still targets Ollama —
32
+ * see `detectFormerOllamaConfig` in `config-store.ts`.
33
+ */
34
+ exports.FORMER_OLLAMA_MODELS = ['bge-m3', 'nomic-embed-text', 'mxbai-embed-large'];
32
35
  /** Advisory dimension for a model, or `undefined` when the model is unknown. */
33
36
  function registryDimension(model) {
34
37
  return exports.MODEL_REGISTRY[model]?.dimensions;
@@ -1,2 +1,2 @@
1
- export { codeChunk, codeDependency, codeSymbol, codeCall, indexMeta } from './schema';
1
+ export { codeChunk, codeDependency, codeSymbol, codeCall, indexMeta, symbolIndexVersion } from './schema';
2
2
  export type { CodeChunkInsert } from './schema';
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.indexMeta = exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
3
+ exports.symbolIndexVersion = exports.indexMeta = exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
4
4
  var schema_1 = require("./schema");
5
5
  Object.defineProperty(exports, "codeChunk", { enumerable: true, get: function () { return schema_1.codeChunk; } });
6
6
  Object.defineProperty(exports, "codeDependency", { enumerable: true, get: function () { return schema_1.codeDependency; } });
7
7
  Object.defineProperty(exports, "codeSymbol", { enumerable: true, get: function () { return schema_1.codeSymbol; } });
8
8
  Object.defineProperty(exports, "codeCall", { enumerable: true, get: function () { return schema_1.codeCall; } });
9
9
  Object.defineProperty(exports, "indexMeta", { enumerable: true, get: function () { return schema_1.indexMeta; } });
10
+ Object.defineProperty(exports, "symbolIndexVersion", { enumerable: true, get: function () { return schema_1.symbolIndexVersion; } });
@@ -430,6 +430,81 @@ export declare const codeSymbol: import("drizzle-orm/pg-core").PgTableWithColumn
430
430
  identity: undefined;
431
431
  generated: undefined;
432
432
  }, {}, {}>;
433
+ container: import("drizzle-orm/pg-core").PgColumn<{
434
+ name: "container";
435
+ tableName: "code_symbol";
436
+ dataType: "string";
437
+ columnType: "PgText";
438
+ data: string;
439
+ driverParam: string;
440
+ notNull: false;
441
+ hasDefault: false;
442
+ isPrimaryKey: false;
443
+ isAutoincrement: false;
444
+ hasRuntimeDefault: false;
445
+ enumValues: [string, ...string[]];
446
+ baseColumn: never;
447
+ identity: undefined;
448
+ generated: undefined;
449
+ }, {}, {}>;
450
+ isStatic: import("drizzle-orm/pg-core").PgColumn<{
451
+ name: "isStatic";
452
+ tableName: "code_symbol";
453
+ dataType: "boolean";
454
+ columnType: "PgBoolean";
455
+ data: boolean;
456
+ driverParam: boolean;
457
+ notNull: true;
458
+ hasDefault: true;
459
+ isPrimaryKey: false;
460
+ isAutoincrement: false;
461
+ hasRuntimeDefault: false;
462
+ enumValues: undefined;
463
+ baseColumn: never;
464
+ identity: undefined;
465
+ generated: undefined;
466
+ }, {}, {}>;
467
+ };
468
+ dialect: "pg";
469
+ }>;
470
+ export declare const symbolIndexVersion: import("drizzle-orm/pg-core").PgTableWithColumns<{
471
+ name: "symbol_index_version";
472
+ schema: undefined;
473
+ columns: {
474
+ project: import("drizzle-orm/pg-core").PgColumn<{
475
+ name: "project";
476
+ tableName: "symbol_index_version";
477
+ dataType: "string";
478
+ columnType: "PgText";
479
+ data: string;
480
+ driverParam: string;
481
+ notNull: true;
482
+ hasDefault: false;
483
+ isPrimaryKey: true;
484
+ isAutoincrement: false;
485
+ hasRuntimeDefault: false;
486
+ enumValues: [string, ...string[]];
487
+ baseColumn: never;
488
+ identity: undefined;
489
+ generated: undefined;
490
+ }, {}, {}>;
491
+ version: import("drizzle-orm/pg-core").PgColumn<{
492
+ name: "version";
493
+ tableName: "symbol_index_version";
494
+ dataType: "number";
495
+ columnType: "PgInteger";
496
+ data: number;
497
+ driverParam: string | number;
498
+ notNull: true;
499
+ hasDefault: false;
500
+ isPrimaryKey: false;
501
+ isAutoincrement: false;
502
+ hasRuntimeDefault: false;
503
+ enumValues: undefined;
504
+ baseColumn: never;
505
+ identity: undefined;
506
+ generated: undefined;
507
+ }, {}, {}>;
433
508
  };
434
509
  dialect: "pg";
435
510
  }>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.indexMeta = exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
3
+ exports.indexMeta = exports.codeCall = exports.symbolIndexVersion = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
4
4
  const pg_core_1 = require("drizzle-orm/pg-core");
5
5
  // NOTE: the `dimensions` literal below is nominal only. `dataType()` is invoked
6
6
  // solely by drizzle-kit DDL generation, which this project does not use — the
@@ -38,6 +38,15 @@ exports.codeSymbol = (0, pg_core_1.pgTable)('code_symbol', {
38
38
  signature: (0, pg_core_1.text)('signature'),
39
39
  line: (0, pg_core_1.integer)('line'),
40
40
  col: (0, pg_core_1.integer)('col'),
41
+ container: (0, pg_core_1.text)('container'),
42
+ isStatic: (0, pg_core_1.boolean)('isStatic').notNull().default(false),
43
+ });
44
+ // Per-project marker of the symbol/call-graph row format. Not one of the four
45
+ // index tables (see the `index-consistency` pruning requirement) — a project
46
+ // with only a marker row is not "indexed" and is never pruned for it.
47
+ exports.symbolIndexVersion = (0, pg_core_1.pgTable)('symbol_index_version', {
48
+ project: (0, pg_core_1.text)('project').primaryKey(),
49
+ version: (0, pg_core_1.integer)('version').notNull(),
41
50
  });
42
51
  exports.codeCall = (0, pg_core_1.pgTable)('code_call', {
43
52
  id: (0, pg_core_1.uuid)('id').primaryKey().defaultRandom(),
@@ -0,0 +1,8 @@
1
+ import type { ChunkPiece } from './interfaces/IChunker';
2
+ import type { DepEdge, SymbolEntry } from './interfaces/IAstParser';
3
+ /**
4
+ * Build the embedding-input prompt for one chunk. `symbols` is the file's
5
+ * top-level declarations only (never members) — the prompt is unaffected by
6
+ * this change (see the `symbol-index-members` non-goal).
7
+ */
8
+ export declare function getEnrichedPrompt(piece: ChunkPiece, relPath: string, serviceName: string, edges: DepEdge[], symbols: SymbolEntry[]): string;